Сохранение позиции прокрутки React Component в Redux при обновлении компонента - PullRequest
1 голос
/ 29 марта 2020

У меня есть компонент React, использующий ловушку для сохранения позиции прокрутки компонента, когда компонент размонтируется. Это прекрасно работает, но не удается при переходе от одного набора данных к другому набору данных без размонтирования компонента.

Например, представьте интерфейс Slack, где есть боковая панель каналов сообщений слева и справа список сообщений (messageList) Если бы вам пришлось перемещаться между двумя каналами, компонент messageList обновлялся бы новым набором данных для messageList, но компонент никогда не был размонтирован, поэтому положение прокрутки никогда не сохранялось.

Я нашел решение, которое работает, но также выдает предупреждение.

Мой текущий хук useEffect для компонента (урезанный) и код, который в настоящее время сохраняет позицию прокрутки при каждом изменении идентификатора messageList:

// Component...

const usePrevious = (value) => {
    const ref = useRef();
    useEffect(() => {
        ref.current = value;
    });
    return ref.current;
};

// Save scroll position when Component unmounts
useEffect(() => {    
    return () => {
        setScrollOffset(parent._id, scrollPos.current);
    };
}, []);

// Save scroll position when Parent ID changes
const oldParent = usePrevious(parent);
if (oldParent && parent._id !== oldParent._id) {
    setScrollOffset(oldParent._id, list ? list.scrollTop : 0);
}

// ...Component

Ошибка это броски: Warning: Cannot update a component from inside the function body of a different component.

И вызывающая его строка - это вызов setScrollOffset внутри последнего блока if. Я предполагаю, что, хотя это работает, я не должен справляться с подобными вещами. Как лучше обрабатывать сохранение позиции прокрутки при изменении значения c для компонента?

Ответы [ 2 ]

1 голос
/ 30 марта 2020

Благодаря предложениям @ drew-reese он указал мне на правильный путь. Приняв его решение (которое ранее я не мог заставить работать должным образом), я смог изолировать свою проблему для использования с реагирующим маршрутизатором. (подключен-реагирует-роутер в моем случае). Проблема заключалась в том, что компонент рендерил и запускал обработчик события onScroll и перезаписывал мою позицию прокрутки, прежде чем я смог ее прочитать.

Для меня решение в конечном итоге состояло в том, чтобы сохранить мой существующий хук useEffect, но потянуть за сохранение смещения прокрутки из него и в useLayoutEffect (пришлось сохранить useEffect, так как есть другие вещи в useEffect, которые я удалил ради сохранения кода примера выше). useLayoutEffect позволило мне прочитать текущую позицию прокрутки до того, как компонент запустил событие onScroll, которое в конечном итоге перезаписало мою сохраненную ссылку на позицию прокрутки до 0.

Это фактически сделало мой код намного чище в целом, убрав необходимость в моем хуке usePrevious полностью. Мой хук useLayoutEffect теперь выглядит следующим образом:

useLayoutEffect(() => {
    return () => {
        setScrollOffset(parent._id, scrollPos.current);
    };
}, [parent._id]);
1 голос
/ 29 марта 2020

Добавить parent._id в массив зависимостей. Рефакторинг кода для кеширования предыдущего родительского идентификатора, добавления его к зависимости и перемещения условного теста внутри эффекта.

Очистка эффекта

Функция очистки запускается до удаления компонента из пользовательского интерфейса, чтобы предотвратить утечки памяти. Кроме того, если компонент визуализируется несколько раз (как обычно), предыдущий эффект очищается перед выполнением следующего эффекта.

// Return previous parent id and cache current
const oldParent = usePrevious(parent);

// Save scroll position when Component unmounts or parent id changes
useEffect(() => {
  if (oldParent && parent._id !== oldParent._id) {
    setScrollOffset(oldParent._id, list ? list.scrollTop : 0);
  }

  return () => {
    setScrollOffset(parent._id, scrollPos.current);
  };
}, [parent._id, oldParent]);

Если это не совсем подходит, тогда используйте два эффекта, один для монтирования / размонтирования, а другой только для обновления родительского идентификатора.

...