Предотвратить ненужную повторную визуализацию дочерних элементов - PullRequest
2 голосов
/ 11 апреля 2019

Я создаю глобальный компонент уведомлений в реагировании, который предоставляет дескриптор createNotification своим дочерним элементам, используя Context.Уведомления отображаются вместе с props.children.Можно ли как-нибудь предотвратить повторный рендеринг props.children, если они не изменились?

Я пытался использовать React.memo и useMemo(props.children, [props.children]), чтобы они не преобладали.

const App = () => {
  return (
    <Notifications>
      <OtherComponent/>
    </Notifications/>
  );
}

const Notifications = (props) => {
  const [notifications, setNotifications] = useState([]);
  const createNotification = (newNotification) => {
    setNotifications([...notifications, ...newNotification]);
  }

  const NotificationElems = notifications.map((notification) => <Notification {...notification}/>);
  return (
    <NotificationContext.Provider value={createNotification}>
      <React.Fragment>
        {NotificationElems}
        {props.children}
      </React.Fragment>
    </NotificationContext.Provider>
  );
};

const OtherComponent = () => {
  console.log('Re-rendered');
  return <button onClick={() => useContext(NotificationContext)(notification)}>foo</button>
}

Каждый раз, когда создается новый notification, props.children перерисовывается, даже если в нем ничего не меняется.Он просто добавляет элементы вдоль него.Это может быть довольно дорого, если у вас большое приложение и все рендеринг для каждого notification, который появляется.Если нет способа предотвратить это, как я могу разделить это, чтобы я мог сделать это:

<div>
  <OtherComponent/>
  <Notifications/>
</div>

и поделиться с OtherComponent дескриптором createNotification?

1 Ответ

3 голосов
/ 11 апреля 2019

Вам нужно использовать хук useCallback для создания вашего createNotification императивного обработчика.В противном случае вы создадите новую функцию при каждом рендеринге компонента Notifications, что приведет ко всем компонентам, потребляющим ваш контекст, для повторного рендеринга, потому что вы всегда передаете новый обработчик всякий раз, когда добавляете уведомление.

Также выскорее всего, это не означает распространение newNotification в массив уведомлений.

Следующее, что вам нужно сделать, это предоставить версию обратного вызова средства обновления для setState внутри setNotifications.Он получает текущий список уведомлений, которые вы можете использовать и добавлять новый.Это делает ваш обратный вызов независимым от текущего значения состояния уведомления.Обновление состояния на основе текущего состояния, как правило, является ошибкой без использования функции средства обновления, так как программа реагирует на несколько обновлений.

const Notifications = props => {
    const [notifications, setNotifications] = useState([]);

    // use the useCallback hook to create a memorized handler
    const createNotification = useCallback(
        newNotification =>
            setNotifications(
                // use the callback version of setState
                notifications => [...notifications, newNotification],
            ),
        [],
    );

    const NotificationElems = notifications.map((notification, index) => <Notification key={index} {...notification} />);

    return (
        <NotificationContext.Provider value={createNotification}>
            <React.Fragment>
                {NotificationElems}
                {props.children}
            </React.Fragment>
        </NotificationContext.Provider>
    );
};

Другая проблема заключается в условном вызове ловушки useContext, которая не разрешена, Крючки должны называться безоговорочно :

const OtherComponent = () => {
    // unconditiopnally subscribe to context
    const createNotification = useContext(NotificationContext);

    console.log('Re-rendered');

    return <button onClick={() => createNotification({text: 'foo'})}>foo</button>;
};

Полностью рабочий пример:

Edit 31wx7q9y75

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...