Mainly for use of library authors, this API allows you to create a custom hook that synchronously reads from an external store.
-
[External library]
externalLibrary.ts- Implements a function
createStorewhich should be used to create a store for your App - Implements a custom hook (
useStore) to access the store (this function returnsuseSyncExternalStore)
- Implements a function
export const createStore = (
initialState,
{ selectors: originalSelectors, effects: originalEffects }
) => {
// ...
const subscribe = (listener) => {
listeners.add(listener);
return () => listeners.delete(listener);
};
// ...
return { getState, setState, subscribe, effects, selectors };
};
export const useStore = (store, selector) =>
useSyncExternalStore(
store.subscribe,
useCallback(() => {
return selector(store.getState());
}, [store, selector])
);-
[Your code]
store.tsYou create a store for your App via a provided
createStorefunctionimport { createStore } from 'externalLibrary'; export const store = createStore( initialState, selectors: { // ... }, effects: { // ... } )
-
[Your code]
App.tsxA component which uses the local store
import { useStore } from 'externalLibrary';
import { store } from "./store";
const App = () => {
const todos = useStore(store, (state) => {
return state.jsonData;
});
// ...
}This example is simplified from this example I found on GitHub
UNDER CONSTRUCTION
const useSyncExternalStore = <T, S>(
subscribe: (callback: () => void) => () => void,
getSnapshot: () => T,
getServerSnapshot?: () => T,
config?: {
isEqual?: (a: T, b: T) => boolean;
getSnapshotBeforeUpdate?: (snapshot: T) => S;
shouldInvalidate?: (prevSnapshot: S, nextSnapshot: S) => boolean;
}
): T;- Using a rxjs
BehaviorSubjectas data source. - Creating a selector with
createSelectorfromreselectlibrary.
// subscribe to changes of rxjs BehaviorSubject
const subscribe = (onStoreChange: () => void) => {
const subscription = stateChanged.subscribe(onStoreChange);
return () => subscription.unsubscribe();
};
// my own store via `useSyncExternalStore`
const useMyStore = ({
data: { dataType = 'liveData', parameterNames, defaultValues },
dataTransform = standardDataTransform,
}) => {
const [selector, initialValues] = createParamsSelector({
data: {
dataType,
parameterNames,
defaultValues,
},
dataTransform,
});
return useSyncExternalStore(subscribe, () => {
const comAPIData = stateChanged.getValue();
if (comAPIData) {
return selector(comAPIData);
}
return initialValues;
});
}Notes:
-
createParamsSelectorhas the signaturecreateParamsSelector: ({ data, dataTransform }) => [Selector, InitialValues] -
stateChangeis defined as as rxjs BehaviorSubjectimport { BehaviorSubject } from 'rxjs'; export const stateChanged = new BehaviorSubject<Data | null>(null);
and data is send to subscribers via:
stateChanged.next(data);
- New in React 18
- It was originally called
useMutableSource. Read about the change here.