Запретить модальное повторное отображение, когда изменяется только модальное содержимое: React Hooks useReducer - PullRequest
2 голосов
/ 30 мая 2020

У меня есть родительско-дочерний компонент Modal и ModalContent в моем приложении React, оба функциональны.

1) Я создал AppContext в приложении. js для доступа к нему глобально во всех компонентах.

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

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

Здесь reducer - довольно простая функция с переключением для modalOpen и функция push / pop в content (array).

2) My Modal Component использует

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, чтобы обновить свойство data content с помощью диспетчеризации, и хранилище редуктора обновляется идеально и возвращает свежий / обновленный:

 {modalOpen: true/false, content: updatedContentArray}

Проблема : всякий раз, когда Я отправляю действие через ModalContent полное состояние возвращается (ожидается) редуктором, и Modal повторно открывается, когда он прослушивает state.modalOpen.

Неудачная попытка : Я попытался специально получить требуемые свойства в соответствующих компонентах. Но компонент Modal по-прежнему выполняет повторную визуализацию, даже если изменяется только содержимое. Есть ли способ посмотреть на специфику c только состояние

Как можно сделать эту работу, только перерисовав ModalContent не Modal.

Изменить: обновлен вопрос с моим макетом (рабочим) кодом редуктора и заявлением об отправке из самого ModalContent.

Ответы [ 2 ]

3 голосов
/ 30 мая 2020

Причина, по которой оба Modal и ModalContent повторно визуализируются при изменении содержимого, заключается в том, что оба компонента используют один и тот же контекст, а при изменении значения контекста все компоненты, слушающие контекст, повторно визуализируются

Способ исправить эту вещь повторного рендеринга - использовать multiple contexts как

 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>

И использовать его как

в модальном режиме. js

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

в модальном содержании. js

const {content, dispatch} = useContext(ContentContext);
1 голос
/ 30 мая 2020

Как сказал @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>
...