React Context is the easiest and most comfortable way to handle global state (or global anything really). It's essentially like Redux but I don't hate it. Much less boilerplate and it Just Works™.
You'll always want to create a context in a separate file and pass the components to wrap in as children. This has multiple reasons:
- most importantly: if you pass the app in as a children prop it won't rerender on every context value change. Every non-prop Provider child and every context consumer will rerender when context values change.
- pulling out all Context functionality allows you to create a clean api. The only things you'll want to export are the ProviderComponent and the useContext function. Both of them should wrap the react api.
interface ContextType {
something: Something | null;
setAssetType: Dispatch<Something | null>;
}
const SomethingContext = React.createContext<ContextType>({
something: null,
setSomething: () => {}
});
const reducer = (oldSomething: Something, newInput: Something) => {
const newSomething = doSomething(newInput, oldSomething)
return newSomething
};
const SomethingContextProvider: React.FC = ({ children }) => {
const [something, setSomething] = React.useReducer(reducer, null);
return (
<SomethingContext.Provider value={{ something, setSomething }}>
{children}
</SomethingContext.Provider>
);
};
const useSomethingContext = () => useContext(SomethingContext);
export { SomethingContextProvider, useSomethingContext };
And then in your app root file, you use the context like so:
const App: React.FC = () => (
<SomethingContextProvider>
<AppContainer />
</SomethingContextProvider>
);
And finally, this is how you actually use the context in a component:
const Consumer: React.FC = () => {
const { something, setSomething } = useSomethingContext();
return (
<div>
{`Current Something: ${something}`}
<button onClick={() => setSomething(newSomethingValue)} />
</div>
);
};