@dr.pogodin/react-global-state
Version:
Hook-based global state for React
90 lines (87 loc) • 3.54 kB
JavaScript
import { createContext, use, useState } from 'react';
import GlobalState from "./GlobalState.js";
import { jsx as _jsx } from "react/jsx-runtime";
const Context = /*#__PURE__*/createContext(null);
/**
* Gets {@link GlobalState} instance from the context. In most cases
* you should use {@link useGlobalState}, and other hooks to interact with
* the global state, instead of accessing it directly.
* @return
*/
export function getGlobalState() {
// TODO: Think about it: on one hand we on purpose called this function
// as getGlobalState(), so that ppl looking for the state hook prefer using
// useGlobalState(), while this getGlobalState() is reserved for nieche cases;
// on the other hand, perhaps we can rename it into useSomething, to both
// follow conventions, and to keep stuff clearly named at the same time.
// eslint-disable-next-line react-hooks/rules-of-hooks
const globalState = use(Context);
if (!globalState) throw new Error('Missing GlobalStateProvider');
return globalState;
}
/**
* @category Hooks
* @desc Gets SSR context.
* @param throwWithoutSsrContext If `true` (default),
* this hook will throw if no SSR context is attached to the global state;
* set `false` to not throw in such case. In either case the hook will throw
* if the {@link <GlobalStateProvider>} (hence the state) is missing.
* @returns SSR context.
* @throws
* - If current component has no parent {@link <GlobalStateProvider>}
* in the rendered React tree.
* - If `throwWithoutSsrContext` is `true`, and there is no SSR context attached
* to the global state provided by {@link <GlobalStateProvider>}.
*/
export function getSsrContext(throwWithoutSsrContext = true) {
const {
ssrContext
} = getGlobalState();
if (!ssrContext && throwWithoutSsrContext) {
throw new Error('No SSR context found');
}
return ssrContext;
}
/**
* Provides global state to its children.
* @param prop.children Component children, which will be provided with
* the global state, and rendered in place of the provider.
* @param prop.initialState Initial content of the global state.
* @param prop.ssrContext Server-side rendering (SSR) context.
* @param prop.stateProxy This option is useful for code
* splitting and SSR implementation:
* - If `true`, this provider instance will fetch and reuse the global state
* from a parent provider.
* - If `GlobalState` instance, it will be used by this provider.
* - If not given, a new `GlobalState` instance will be created and used.
*/
const GlobalStateProvider = ({
children,
...rest
}) => {
const [localState, setLocalState] = useState();
let state;
// Below we cast `rest.stateProxy` as "boolean" for safe backward
// compatibility with plain JavaScript (as TypeScript typings only
// permit "true" or GlobalState value; while legacy codebase may
// pass in a boolean value here, occasionally equal "false").
if ('stateProxy' in rest && rest.stateProxy) {
if (localState) setLocalState(undefined);
state = rest.stateProxy === true ? getGlobalState() : rest.stateProxy;
} else if (localState) {
state = localState;
} else {
const {
initialState,
ssrContext
} = rest;
state = new GlobalState(typeof initialState === 'function' ? initialState() : initialState, ssrContext);
setLocalState(state);
}
return /*#__PURE__*/_jsx(Context, {
value: state,
children: children
});
};
export default GlobalStateProvider;
//# sourceMappingURL=GlobalStateProvider.js.map