Безопасно ли использовать Memo для JSX? - PullRequest
2 голосов
/ 28 февраля 2020

Я пытаюсь сделать памятку Modal, и у меня здесь проблема.

При изменении ввода мне не нужно повторно визуализировать Модальный компонент.

Например:

Modal.tsx выглядит так:

import React from "react";
import { StyledModalContent, StyledModalWrapper, AbsoluteCenter } from "../../css";

interface ModalProps {
  open: boolean;
  onClose: () => void;
  children: React.ReactNode
};

const ModalView: React.FC<ModalProps> = ({ open, onClose, children }) => {
  console.log("modal rendered");

  return (
    <StyledModalWrapper style={{ textAlign: "center", display: open ? "block" : "none" }}>
      <AbsoluteCenter>
        <StyledModalContent>
          <button
            style={{
              position: "absolute",
              cursor: "pointer",
              top: -10,
              right: -10,
              width: 40,
              height: 40,
              border: 'none',
              boxShadow: '0 10px 10px 0 rgba(0, 0, 0, 0.07)',
              backgroundColor: '#ffffff',
              borderRadius: 20,
              color: '#ba3c4d',
              fontSize: 18
            }}
            onClick={onClose}
          >
            X
          </button>
          {open && children}
        </StyledModalContent>
      </AbsoluteCenter>
    </StyledModalWrapper>
  );
};

export default React.memo(ModalView);

Вот пример того, как я обертываю это.

import React from 'react'
import Modal from './modal;

const App: React.FC<any> = (props: any) => {
  const [test, setTest] = React.useState("");
  const [openCreateChannelDialog, setOpenCreateChannelDialog] = React.useState(false);

  const hideCreateModalDialog = React.useCallback(() => {
    setOpenCreateChannelDialog(false);
  }, []);

  return (
    <>
      <input type="text" value={test} onChange={(e) => setTest(e.target.value)} />
      <button onClick={() => setOpenCreateChannelDialog(true)}>Create channel</button>

      <Modal
        open={openCreateChannelDialog}
        onClose={hideCreateModalDialog}
        children={<CreateChannel onClose={hideCreateModalDialog} />}
      />
    </>
};

Я знаю, Modal перерисовывается, потому что children ссылка создается каждый раз, когда App компонент перерисовывается (когда я изменяю текст ввода).

Знайте, мне интересно, если я оберну <CreateChannel onClose={hideCreateModalDialog} /> внутри React.useMemo () hook

Например:

  const MemoizedCreateChannel = React.useMemo(() => {
    return <CreateChannel onClose={hideCreateModalDialog} />
  }, [hideCreateModalDialog]);

И поменять дочерние реквизиты внутри Modal

с:

children={<CreateChannel onClose={hideCreateModalDialog} />}

до

children={MemoizedCreateChannel}

Работает нормально, но я в безопасности? И это единственное решение, что я пытаюсь запомнить модал?

Ответы [ 2 ]

1 голос
/ 25 апреля 2020

Запоминание выражений JSX является частью официального useMemo API :

const Parent = ({ a }) => useMemo(() => <Child1 a={a} />, [a]); 
// This is perfectly fine; Child re-renders only, if `a` changes

useMemo запоминает отдельные дочерние и вычисленные значения с учетом любых зависимостей. Вы можете думать о memo как о ярлыке useMemo для всего компонента, который сравнивает все реквизиты .

Но у memo есть один недостаток - это не так t работа с детьми :

const Modal = React.memo(ModalView);

// React.memo won't prevent any re-renders here
<Modal>
  <CreateChannel />
</Modal>

children являются частью реквизита. И React.createElement всегда создает новую неизменяемую ссылку на объект ( REPL ). Поэтому каждый раз, когда memo сравнивает реквизиты, он определяет, что ссылка children изменилась, если не является примитивом.

Чтобы предотвратить это, вы можете использовать useMemo в родительском App, чтобы запомнить children (что вы уже сделали). Или определите пользовательскую функцию сравнения для memo, чтобы компонент Modal теперь стал отвечать за оптимизацию производительности. react-fast-compare - это удобная библиотека, позволяющая избежать использования котельной плиты для areEqual.

0 голосов
/ 28 февраля 2020

Это безопасно? Да. В конце дня JSX просто преобразуется в JSON объект, который совершенно хорошо запомнить.

Тем не менее, я думаю, что это стилистически немного странно, и я мог предвидеть это может привести к неожиданным ошибкам в будущем, если что-то изменится, и вы не продумаете это полностью.

...