I have a parent-child component Modal and ModalContent in my React App both functional.
1) I have created an AppContext in App.js for accessing it globally in all components.
const [state, dispatch] = useReducer(reducer, {modalOpen: false, content: []});
<AppContext.Provider value={{ state, dispatch }} >
//BrowserRouter and Routes Definition
</App>
Here reducer is a fairly plain function with toggle for modalOpen and push/pop functionality in content(array).
2) My Modal
Component makes use of
const { state, dispatch } = useContext(AppContext);
<Modal open={state.modalOpen} />
to get the state of modal visibility to set it open/closed.
3) My ModalContent
Component makes use of
const { state, dispatch } = useContext(AppContext);
<ModalContent data={state.content} />
//Dispatch from the `ModalContent`
dispatch({ type: 'UPDATE_CONTENT', data: 'newata' });
4) Here is my reducer.
export const reducer = (state, action) => {
switch (action.type) {
case 'TOGGLE_MODAL':
return {...state, modalOpen: !state.modalOpen};
case 'UPDATE_CONTENT':
return { ...state, content: [...state.content, action.data]};
default:
return {modalOpen: false,content: []};
}
}
I have set up some code in ModalContent
to update the data content
property using dispatch and the reducer store is updated perfectly and returns fresh/updated:
{modalOpen: true/false, content: updatedContentArray}
The issue is: Whenever I dispatch an action via ModalContent
complete state is returned(expected), by the reducer and Modal reopens itself as it listens to state.modalOpen.
Unsuccessful Attempt: I attempted to specifically pull required properties in respective components. But the Modal
component still rerenders even when just content is changed. Is there any way to watch for specific state only
How can make this work by rerendering only ModalContent
not Modal
.
Edit: Updated question with my mock(working) reducer code and dispatch statement from the ModalContent itself.
The reason both Modal
and ModalContent
get re-rendered when Content changes is because both the components make use of the same context and when a context value changes all components listening to the context are re-rendered
A way to fix this re-rendering thing is to make use of multiple contexts
like
const modalContextVal = useMemo(() => ({ modalOpen: state.modalOpen, dispatch}), [state.modalOpen]);
const contentContextVal = useMemo(() => ({ content: state.content, dispatch}), [state.content]);
....
<ModalContext.Provider value={modalContextVal}>
<ContentContext.Provider value={contentContextVal}>
//BrowserRouter and Routes Definition
</ContentContext.Provider>
</ModalContext.Provider>
And use it like
In Modal.js
const {modalOpen, dispatch} = useContext(ModalContext);
In ModalContent.js
const {content, dispatch} = useContext(ContentContext);