Автоматическое сохранение состояния в React (KeepAlive)

React.js

Картина и текст не имеют ничего общего

Что такое консервация штата?

Предположим следующий сценарий:

В мобильном терминале пользователь посещает страницу со списком, и в процессе прокрутки вверх и просмотра страницы со спискомвысота прокруткиПостепенно увеличивайте, данные также будут использовать днопостраничная загрузкаФорма постепенно увеличивается, просмотр страницы списка в месте, пользователь видит интерес элемент, нажмите, чтобы просмотреть детали, введите страницу сведений,При возврате на страницу списка со страницы сведений вам необходимо оставаться в позиции просмотра при выходе со страницы списка.

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

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

Как реализовать сохранение состояния в React

существуетVue, мы можем легко пройти<keep-alive>Тег реализует сохранение состояния, которое кэширует экземпляры неактивных компонентов, а не уничтожает их.

пока вReactВ нем нет такой функции, кто-то упомянул об этом в официальномissues, но чиновник считает, что эта функция может вызвать утечку памяти, указывая на то, что поддержка пока не рассматривается, поэтому нам нужно найти способ самостоятельно

Распространенное решение: сохранить состояние вручную

Сохранить состояние вручную, является относительно распространенным решением, которое можно комбинировать сReactкомпонентcomponentWillUnmountжизненный цикл черезreduxУровень управления состоянием, такой как уровень управления состоянием, сохраняет данные посредствомcomponentDidMountПериодическое восстановление данных

Когда есть несколько состояний, которые нужно сохранить, этот метод может относительно быстро достичь нужных нам функций, но когда объем данных велик или ситуация изменчива, ручное сохранение состояния станет хлопотным делом.

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

Автоматическое сохранение состояния через маршрутизацию (обычно с использованиемreact-router)

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

У нас есть следующие способы достижения этой функции

  1. переписать<Route>компоненты, вы можете обратиться кreact-live-route

    Переписывание может обеспечить желаемую функцию, но стоимость относительно высока, нам нужно обратить внимание на оригинал<Route>сохранение функции и многократноеreact-routerсовместимость версий

  2. Переписать библиотеку маршрутизации, вы можете обратиться кreact-keeper

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

  3. на основе<Route>Расширить существующее поведение компонента, вы можете обратиться кreact-router-cache-route

    во время чтения<Route>После исходного кода найдите, что если используетсяcomponentилиrenderатрибут, не может избежать участи удаления маршрута, если он не соответствует

    но будетchildrenАтрибуты используются как методы, у нас есть возможность вручную управлять поведением рендеринга, код ключа здесьGitHub.com/реагировать поезд я…

      // 节选自 Route 组件中的 render 函数
      if (typeof children === "function") {
        children = children(props); // children 是函数时,将对 children 进行调用得到真实的渲染结果
    
        if (children === undefined) {
          ...
    
          children = null;
        }
      }
    
      return (
        <RouterContext.Provider value={props}>
          {children && !isEmptyChildren(children) 
            ? children // children 存在时,将使用 children 进行渲染
            : props.match
              ? component
                ? React.createElement(component, props)
                : render
                  ? render(props)
                  : null // 使用 render 属性无法阻止组件的卸载
              : null // 使用 component 属性无法阻止组件的卸载
          }
        </RouterContext.Provider>
      );
    

    Основываясь на приведенном выше исследовании исходного кода, мы можем<Route>Развернуть, будет<Route>несоответствие поведенияИзменено с удаления на скрытый,следующим образом

    <Route exact path="/list">
        {props => (
            <div style={props.match ? null : { display: 'none' }}>
                <List {...props} />
            </div>
        )}
    </Route>
    

    Выше описан самый простой способ настройки, в реальных ситуациях также необходимо учитывать скрытое состояние.matchдляnullПроблема, из-за которой компонент сообщает об ошибке, и поскольку компонент больше не удаляется, иTransitionGroupКоординация не очень хорошая, что затрудняет анимацию перехода.

    использоватьreact-router-cache-route, полученный эффект примерно такой, как показано ниже,

Вышеизложенное исследует возможность автоматического сохранения состояния через маршрутизацию, а также существующие реализации, но в конце концов это не реально и не чисто.KeepAliveфункция, то мы пытаемся исследовать реальнуюKeepAliveРеализация функции

имитировать реальный<KeepAlive>Функция

Вот ожидаемое использование

function App() {
  const [show, setShow] = useState(true)

  return (
    <div>
      <button onClick={() => setShow(show => !show)}>Toggle</button>
      {show && (
        <KeepAlive>
          <Test />
        </KeepAlive>
      )}
    </div>
  )
}

Принцип реализации относительно прост, посколькуReactвыгружает компоненты во встроенной иерархии компонентов, поэтому нам нужно<KeepAlive>компоненты вchildrenАтрибуты извлекаются и отображаются в компоненте, который не будет удален.<Keeper>внутри, использовать сноваDOMоперация будет<Keeper>Настоящий контент внутри перемещается в соответствие<KeepAlive>, вы можете выполнить эту функцию

Вот самый простой, меньше 70 строк<KeepAlive>Пример реализации:минимальная реализация

Ниже приведеныreact-activationэффект реализации

Онлайн-пример

На картинке ниже<KeepAlive>Принцип реализации

В процессе фактической реализации возникло много проблем, все из-за нарушения исходного кода.Reactобусловлены иерархическими отношениями, такими как

  • задержка рендеринга (react-activationбыло исправлено)
  • ContextФункция контекста не работает (react-activationбыло исправлено)
  • Error Boundariesневерный (react-activationбыло исправлено)
  • React.Suspense & React.lazyневерный (react-activationбыло исправлено)
  • Сбой всплытия синтетических событий React
  • Другие неизвестные функции

Тем не менее, большинство из вышеперечисленных проблем можно устранить с помощью механизма моста.Подробности см. здесь.issues

Такая же, более ранняя реализация также имеетreact-keep-alive

Эпилог

Кэширование состояния является очень распространенным требованием в приложениях.Когда объем обрабатываемых данных невелик, большинство проблем можно решить с помощью ручного кэширования состояния.Однако, когда ситуация сложная, необходимо попытаться решить функцию кэширования отдельно, так что Лучшее разделение интересов во время развития бизнеса

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