admin管理员组文章数量:1390618
I have a parent-child ponent Modal and ModalContent in my React App both functional.
1) I have created an AppContext in App.js for accessing it globally in all ponents.
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
plete 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 ponents. But the Modal
ponent 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.
I have a parent-child ponent Modal and ModalContent in my React App both functional.
1) I have created an AppContext in App.js for accessing it globally in all ponents.
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
plete 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 ponents. But the Modal
ponent 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.
Share Improve this question edited May 30, 2020 at 14:01 nikhil024 asked May 30, 2020 at 13:33 nikhil024nikhil024 4798 silver badges22 bronze badges 5- 1 can you show your reducer and the dispatch call. Also is the issue just that Modal is re-rendering or that the openModal is incorrectly getting modelOpen value as true – Shubham Khatri Commented May 30, 2020 at 13:37
-
You could enhance
Modal
withReact.memo
to be a memoized ponent, which won't re-render, ifstate.modalOpen
has the same value. – ford04 Commented May 30, 2020 at 14:00 - @ShubhamKhatri I have added my simpler version of my reducer(working) and added a dispatch call in the code snippet above. The openModal is working fine as I can see when I close the modal it closes and state updates to false for that. – nikhil024 Commented May 30, 2020 at 14:08
- ok, so the issue is just the re-rendering of Modal ponent when only content changes? – Shubham Khatri Commented May 30, 2020 at 14:10
- Yes @ShubhamKhatri – nikhil024 Commented May 30, 2020 at 14:14
2 Answers
Reset to default 3The reason both Modal
and ModalContent
get re-rendered when Content changes is because both the ponents make use of the same context and when a context value changes all ponents 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);
As @Shubham said you have to keep modal state and modal content separate.
It can be done with separate context or even simple useState
Example snippet
const { useReducer, createContext, useContext, useState, useEffect, memo, useMemo } = React;
const AppContext = createContext();
const reducer = (state, action) => {
if(action.type == 'toggleModal') {
return {
...state,
modalOpen: !state.modalOpen
}
}
return state;
}
const AppContextProvider = ({children}) => {
const [state, dispatch] = useReducer(reducer, {modalOpen: false, content: [{id: 1, value: 'test'}]});
return <AppContext.Provider value={{state, dispatch}}>
{children}
</AppContext.Provider>
}
const Modal = ({children, modalOpen}) => {
const { state, dispatch } = useContext(AppContext);
console.log('Render Modal');
return <div className={`modal ${modalOpen ? 'modal--open': null}`}>
{children}
</div>
}
const ModalContent = ({data, onClick}) => {
console.log('Render Modal Content');
return <div className="modal__content">
{data.map(({id, value}) => <div className="item" key={id}>{value}</div>)}
<button onClick={onClick} className="modal__close">Close</button>
</div>
}
const App = () => {
const { state, dispatch } = useContext(AppContext);
const { modalOpen, } = state;
const [content, setContent] = useState([]);
const onClick = () => {
dispatch({ type: 'toggleModal' });
}
return <div>
<Modal modalOpen={modalOpen}>
{useMemo(() => {
console.log('render useMemo');
return <ModalContent onClick={onClick} data={content}></ModalContent>
}, [content])}
</Modal>
<button onClick={onClick}>Open Modal</button>
</div>
}
ReactDOM.render(
<AppContextProvider>
<App />
</AppContextProvider>,
document.getElementById('root')
);
.modal {
background: black;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
z-index: -1;
transition: all .3s ease-in;
}
.modal__close {
padding: .5rem 1rem;
color: white;
border: 1px solid white;
background: transparent;
cursor: pointer;
}
.modal--open {
opacity: 1;
z-index: 1;
}
.item {
padding: 1rem;
color: white;
border: 1px solid white;
}
<script src="https://unpkg./react/umd/react.development.js"></script>
<script src="https://unpkg./react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg./babel-standalone@6/babel.min.js"></script>
<div id="root"></div>
版权声明:本文标题:javascript - Prevent Modal Rerendering when only Modal Content Change: React Hooks useReducer - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744739993a2622550.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论