@rn-tools/core 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/store.tsx ADDED
@@ -0,0 +1,82 @@
1
+ import * as React from "react";
2
+ import useSyncExternalStoreExports from "use-sync-external-store/shim/with-selector";
3
+
4
+ const { useSyncExternalStoreWithSelector } = useSyncExternalStoreExports;
5
+
6
+ export type Store<T> = {
7
+ getState: () => T;
8
+ getInitialState: () => T;
9
+ setState: (nextState: T | ((prevState: T) => T)) => void;
10
+ subscribe: (listener: () => void) => () => void;
11
+ };
12
+
13
+ export type StoreApi<T> = Store<T>;
14
+
15
+ export function createStore<T>(initialState: T): Store<T> {
16
+ const listeners = new Set<() => void>();
17
+ let state = initialState;
18
+
19
+ const getState = () => state;
20
+ const getInitialState = () => initialState;
21
+
22
+ const setState = (nextState: T | ((prevState: T) => T)) => {
23
+ const resolved =
24
+ typeof nextState === "function"
25
+ ? (nextState as (prevState: T) => T)(state)
26
+ : nextState;
27
+
28
+ if (Object.is(resolved, state)) {
29
+ return;
30
+ }
31
+
32
+ state = resolved;
33
+ listeners.forEach((listener) => listener());
34
+ };
35
+
36
+ const subscribe = (listener: () => void) => {
37
+ listeners.add(listener);
38
+ return () => listeners.delete(listener);
39
+ };
40
+
41
+ return { getState, getInitialState, setState, subscribe };
42
+ }
43
+
44
+ export function useStore<T, S = T>(
45
+ store: Store<T>,
46
+ selector: (state: T) => S = (state) => state as unknown as S,
47
+ isEqual: (left: S, right: S) => boolean = Object.is,
48
+ ): S {
49
+ const slice = useSyncExternalStoreWithSelector(
50
+ store.subscribe,
51
+ store.getState,
52
+ store.getInitialState,
53
+ selector,
54
+ isEqual,
55
+ );
56
+ React.useDebugValue(slice);
57
+ return slice;
58
+ }
59
+
60
+ export const StoreContext = React.createContext<Store<unknown> | null>(null);
61
+
62
+ export function StoreProvider<T>(props: {
63
+ store: Store<T>;
64
+ children: React.ReactNode;
65
+ }) {
66
+ return (
67
+ <StoreContext.Provider value={props.store}>
68
+ {props.children}
69
+ </StoreContext.Provider>
70
+ );
71
+ }
72
+
73
+ export function useStoreContext<T, S = T>(
74
+ selector?: (state: T) => S,
75
+ isEqual?: (left: S, right: S) => boolean,
76
+ ) {
77
+ const store = React.useContext(StoreContext) as Store<T> | null;
78
+ if (!store) {
79
+ throw new Error("StoreProvider is missing from the component tree.");
80
+ }
81
+ return useStore(store, selector, isEqual);
82
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "compilerOptions": {
3
+ "baseUrl": ".",
4
+ "paths": {
5
+ "~/*": ["src/*"]
6
+ }
7
+ },
8
+ "extends": "expo/tsconfig.base",
9
+ "include": ["**/*.ts", "**/*.tsx"]
10
+ }
@@ -0,0 +1,21 @@
1
+ import path from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ import { defineConfig } from "vitest/config";
4
+
5
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+
7
+ export default defineConfig({
8
+ resolve: {
9
+ alias: {
10
+ "react-native": path.resolve(
11
+ __dirname,
12
+ "mocks/react-native.mock.ts",
13
+ ),
14
+ },
15
+ },
16
+ test: {
17
+ environment: "jsdom",
18
+ globals: true,
19
+ setupFiles: ["mocks/setup.ts"],
20
+ },
21
+ });