2

我的 React App 中有一个父子组件ModalModalContent都可以使用。

1) 我在 App.js 中创建了一个 AppContext,用于在所有组件中全局访问它。

const [state, dispatch] = useReducer(reducer, {modalOpen: false, content: []});

    <AppContext.Provider value={{ state, dispatch }} >
      //BrowserRouter and Routes Definition
    </App>

这里的reducer是一个相当简单的函数,带有用于modalOpen和内容(数组)中的推送/弹出功能的切换。

2)我的Modal组件利用

const { state, dispatch } = useContext(AppContext); 
<Modal open={state.modalOpen} />

获取模态可见性的状态以将其设置为打开/关闭。

3)我的ModalContent组件利用

const { state, dispatch } = useContext(AppContext); 
<ModalContent data={state.content} />
  //Dispatch from the `ModalContent`
  dispatch({ type: 'UPDATE_CONTENT', data: 'newata' });

4)这是我的减速机。

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: []};
 }
} 

我已经设置了一些代码ModalContent来使用调度更新数据content属性,并且减速器存储完美更新并返回新鲜/更新:

 {modalOpen: true/false, content: updatedContentArray}

问题是:每当我通过ModalContent完成状态调度一个动作时,reducer 会返回(预期),并且 Modal 在侦听 state.modalOpen 时会重新打开。

不成功的尝试:我试图在各个组件中专门提取所需的属性。但是Modal即使仅更改内容,组件仍会重新呈现。有没有办法只关注特定的状态

如何通过仅ModalContent不重新渲染来完成这项工作Modal

编辑:用我的模拟(工作)reducer 代码和来自 ModalContent 本身的调度语句更新了问题。

4

2 回答 2

3

ModalContent 更改时ModalContent重新渲染的原因是因为两个组件都使用相同的上下文,并且当上下文值更改时,所有侦听上下文的组件都会重新渲染

解决此重新渲染问题的一种方法是使用multiple contextslike

 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>

并像使用它一样

在 Modal.js 中

const {modalOpen, dispatch} = useContext(ModalContext);

在 ModalContent.js 中

const {content, dispatch} = useContext(ContentContext);
于 2020-05-30T14:24:23.040 回答
1

正如@Shubham 所说,您必须将模态状态和模态内容分开。

它可以用单独的上下文甚至简单的 useState 来完成

示例片段

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.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<div id="root"></div>

于 2020-05-30T14:54:26.823 回答