ReactJS: Как синхронизировать состояние sessionStorage между компонентами - PullRequest
2 голосов
/ 22 апреля 2019

В моем приложении есть компонент React, который отображает список чисел, а также хранит сумму этих чисел через sessionStorage.

В моем приложении также есть компонент с <input />, чтобы можно было добавлять новые номера.Это также приводит к обновлению значения, хранящегося в sessionStorage.Для каждого числа существует button, позволяющее удалять числа, и это немедленно обновляет значение, сохраненное в sessionStorage.

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

Я пытаюсь обновить его, используя useEffect(), но он не работает:

import React from 'react';
import { useState, useEffect } from "react";


const LimitsIndicator = props => {
  const {
    limits: { total, used },
    currency,
    t,
    type,
  } = props;


  const [limitInUse, setInUse] = useState(sessionStorage.getItem('inUse'));

  useEffect(() => {
    setInUse(sessionStorage.getItem('inUse'))
  })

  return (
    <div>{limitInUse}</div>
  )

}

На этом изображении показана сумма: 250 и два значения:100 и 150, но значение 150 было отменено, и, как вы можете видеть на консоли, sessionStorage обновляется, но значение суммы не меняется.

enter image description here

Ответы [ 2 ]

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

Несколько недель назад у меня возникла похожая проблема, и я создал пакет NPM для этой цели: совместное использование состояний между компонентами через Context API и т. Д. автоматически с сохранением его в localStorage.Посмотрите и попробуйте: он называется repersist .

Пример.

1.Вы начинаете с определения вашего общего постоянного состояния в файле конфигурации:

import repersist from 'repersist'

const { Provider, useStore } = repersist({
  storageKey: 'mystorekey',

  init: {
    counter: 0,
    search: ''
  },

  actions: {
    increment: () => ({ counter}) => ({
      counter: counter + 1
    }),
    typeSearch: search => ({ search })
  }
})

export { Provider, useStore }

2.Введите состояние через контекст:

import { Provider } from './storeConfig'

ReactDOM.render(
  <Provider>
    <App/>
  </Provider>,
  document.getElementById('root')
)

3.Используйте его везде, где захотите. Каждое изменение вызывает обновление React и a localStorage, и это именно то, что вам нужно.Ваше localStorage в основном синхронизируется независимо от того, что.

import { useStore } from './storeConfig'

const SearchField = () => {
  const [{ search }, { typeSearch }] = useStore()
  return (
    <input value={search} onChange={e => typeSearch(e.target.value)}/>
  )
}

Каждый раз, когда ваша страница обновляется, состояние восстанавливается localStorage.

1 голос
/ 24 апреля 2019

Один из способов добиться синхронизации состояний между различными частями вашего приложения - использовать React's Context API .

Общая идея заключается в том, чтобы централизовать общее состояние (т. Е. limitInUse) в (или около) корневом компоненте вашего приложения через провайдера контекста, а затем обернуть дочерние компоненты, которым необходим доступ к общему состоянию черезсоответствующий контекст потребителя:

1.Создайте контекст для общего состояния

Создайте контекст, который дает нам состояние «поставщик» и состояние «потребитель».Потребитель контекста будет использоваться для доступа и взаимодействия с общим состоянием в приложении:

const IsUseContext = React.createContext();

2.Определите доступ к состоянию для общего состояния в корневом компоненте

Далее определите логику получения / установки для общего состояния limitInUse.Это должно быть определено в состоянии на (или около) корневом уровне вашего приложения.Здесь я определяю это в корневых компонентах state объекта:

this.state = {
  /* Define initial value for shared limitInUse state */
  limitInUse : sessionStorage.getItem('inUse'),

  /* Define setter method by which limitInUse state is updated */
  setLimitInUse: (inUse) => {

    /* Persist data to session storage, and update state to trigger re-render */      
    sessionStorage.setItem('inUse', `${ inUse }`)
    this.setState({ limitInUse })
  },
}

3.Визуализация провайдера контекста из корневого компонента

Теперь визуализируйте компонент контекста Provider на корневом уровне вашего приложения и передайте объект state с помощью провайдера value prop.Объект state теперь будет доступен из любого потребителя контекста, используемого в приложении (см. Ниже):

/* In your app component's render() method, wrap children with context provider */
<IsUseContext.Provider value={ this.state }>
  <LimitsIndicator />
  <InputComponent />
</IsUseContext.Provider>

4.Обновление общего состояния в дочернем компоненте

Наконец, мы получаем доступ к общим limitInUse из вложенных дочерних компонентов нашего приложения через потребителя нашего контекста.Обратите внимание, что состояние и метод установки, определенные в объекте, переданном в value prop нашего провайдера, доступны и доступны:

/* Update shared limitInUse state via context consumer */
return (<IsUseContext.Consumer>
  { ({ setLimitInUse }) => <input onChange={ 
     (event) => setLimitInUse(event.currentTarget.value) } />
  }
</IsUseContext.Consumer>)

Отображение общего состояния в дочернем компоненте

/* Access shared limitInUse via context consumer */
return (<IsUseContext.Consumer>
  { ({ limitInUse }) => (<div>  {limitInUse} </div>) }
</IsUseContext.Consumer>)

Для полной рабочей демонстрации см. this jsFiddle .Надеюсь, это поможет!

...