I'm working on some code, and this code is huge. We have so many child components(nearly 300) and each one of them are using & manipulating values from the parent component's state via React Context.
Note: I didn't wrote this code from scratch. Which also means the design I'm about to show is not what I would come up with.
The problem: Since every component is using the same state, there are so many unnecessary re-renders happening. Every small state change is causing every component to re-render. And it makes the web app laggy. Literally, there is lag when you enter some input in a field.
I think, when state change happens the functions get rebuilt and that's why every child gets updated, because the value provided by context is changed after state change happened.
Additionally, I tried to use useReducer
instead of useState
that didn't went well. Besides that, I tried to use React.memo
on every child component but the compare
function didn't get triggered no matter what I tried. compare
function only got triggered on the parent component which has the state as props. At this point, I'm not even really sure what is the problem :D
To give more specific details on the design, here is how we define and pass the callbacks to child components.
Definitions:
const [formItemState, setFormState] = React.useState<FormItemState>({} as FormItemState);
const getAppState = useCallback(() => ({ state: props.state as AppState }), [props.state]);
const getAppAction = useCallback(() => ({ action: props.action as AppAction }), [props.action]);
const getFormItemError = useCallback((key: string) => formItemErrorState[key], [
formItemErrorState,
]);
const getFormItem = useCallback((key: string) => formItemState[key], [formItemState]);
const updateFormItem = useCallback(
(name: string, formItemData: FormItemData): void => {
const previousState = getFormItem(name);
if (!isEqual(previousState, formItemData)) {
formItemState[name] = formItemData;
setFormState((state) => ({
...state,
...formItemState,
}));
}
},
[formItemState, setFormState]
);
Passing them to Context.Provider
:
return (
<FormContext.Provider
value={{
getAppAction,
getAppState,
getFormItem,
updateFormItem
}}
>
<SomeComponent>
{props.children} // This children contains more than 250 nested components, and each one of them are using these provided functions to interact with the state.
</SomeComponent>
</FormContext.Provider>
);
Last note: Please ask me if more info is needed. Thanks!
Why dont you just rewrite your state management to redux and pull only the necessary state to be used on each component. React.memo only picks up changes from props