ReactJS:如何在组件之间同步 sessionStorage 状态

ReactJS: How to synchronize sessionStorage state between components

在我的应用程序中,我有一个呈现数字列表的 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 已更新,但是总和的值不变。

在应用的不同部分之间实现状态同步的一种方法是通过 React 的 Context API

总体思路是通过上下文提供程序将共享状态(即 limitInUse)集中在应用程序的根组件(或附近),然后包装需要访问共享状态的子组件相应的上下文消费者:

1.为共享状态创建上下文

创建一个上下文,为我们提供一个状态 "provider" 和一个状态 "consumer"。上下文使用者将用于访问整个应用程序中的共享状态并与之交互:

const IsUseContext = React.createContext();

2。在根组件中定义共享状态的状态访问

接下来,为共享 limitInUse 状态定义 get/set 逻辑。这应该在应用程序根级别(或附近)的状态中定义。在这里,我在根组件 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 组件,并通过提供者的 value 属性传递 state 对象。 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 道具的对象中定义的状态和 setter 方法可用且可访问:

/* 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。希望这对您有所帮助!

几周前,我遇到了类似的问题并创建了一个 NPM 程序包来做到这一点:通过上下文 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 一个 localStorage 更新,而这正是您想要的。无论如何,您的 localStorage 基本上是同步的。

import { useStore } from './storeConfig'

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

每次您的页面刷新时,状态都会通过 localStorage 重新水化。