如何在挂载/卸载之间保持 React 组件状态?

IT技术 javascript reactjs
2021-04-04 23:05:07

我有一个<StatefulView>维护内部状态的简单组件我有另一个组件<App>可以切换是否<StatefulView>呈现。

但是,我想<StatefulView>在安装/卸载之间保持的内部状态。

我想我可以在其中实例化组件<App>,然后控制它是否呈现/安装。

var StatefulView = React.createClass({
  getInitialState: function() {
    return {
      count: 0
    }
  },
  inc: function() {
    this.setState({count: this.state.count+1})
  },
  render: function() {
    return (
        <div>
          <button onClick={this.inc}>inc</button>
          <div>count:{this.state.count}</div>
        </div>
    )
  }
});

var App = React.createClass({
  getInitialState: function() {
    return {
      show: true,
      component: <StatefulView/>
    }
  },
  toggle: function() {
    this.setState({show: !this.state.show})
  },
  render: function() {
    var content = this.state.show ? this.state.component : false
    return (
      <div>
        <button onClick={this.toggle}>toggle</button>
        {content}
      </div>
    )
  }
});

这显然不起作用,<StatefulView>每次切换都会创建一个新的

这是一个JSFiddle

有没有办法在卸载后挂在同一个组件上以便重新安装?

6个回答

由于您无法在卸载时将状态保留在组件本身中,因此您必须决定将其保存在何处。

这些是您的选择:

  1. React state in parent:如果父组件保持挂载,它可能应该是状态的所有者,或者可以为下面的不受控制的组件提供初始状态。您可以在卸载组件之前将值传递回来。使用 React 上下文,您可以将状态提升到应用程序的最顶部(参见例如unstated)。
  2. 在 React 之外:例如use-local-storage-state请注意,您可能需要在测试之间手动重置状态。其他选项是 URL 中的查询参数、状态管理库,如 MobX 或 Redux 等。

我一直在寻找一个简单的解决方案,将数据持久化到 React 之外,这个 Hook 可能会派上用场:

const memoryState = {};

function useMemoryState(key, initialState) {
  const [state, setState] = useState(() => {
    const hasMemoryValue = Object.prototype.hasOwnProperty.call(memoryState, key);
    if (hasMemoryValue) {
      return memoryState[key]
    } else {
      return typeof initialState === 'function' ? initialState() : initialState;
    }
  });

  function onChange(nextState) {
    memoryState[key] = nextState;
    setState(nextState);
  }

  return [state, onChange];
}

用法:

const [todos, setTodos] = useMemoryState('todos', ['Buy milk']);
只是向其他人发出警告,这不适用于测试或在多个地方使用的组件。
2021-06-11 23:05:07

行。所以和一堆人聊了聊,发现一个组件的实例是没有办法保存的。因此,我们必须将它保存在别处。

1) 保存状态最明显的地方是在父组件内。

这对我来说不是一个选择,因为我试图从类似 UINavigationController 的对象中推送和弹出视图。

2) 您可以将状态保存在其他地方,例如 Flux 商店或某个全局对象。

这对我来说也不是最好的选择,因为跟踪哪些数据属于哪个视图、哪个导航控制器等将是一场噩梦。

3)传递一个可变对象来保存和恢复状态。

这是我在 React 的 Github 存储库的各种问题单中发表评论时发现的一个建议。这似乎是我要走的路,因为我可以创建一个可变对象并将其作为props传递,然后使用相同的可变props重新渲染相同的对象。

我实际上对其进行了一些修改以使其更通用,并且我使用的是函数而不是可变对象。我认为这更明智——不可变数据总是对我更可取。这是我在做什么:

function createInstance() {
  var func = null;
  return {
    save: function(f) {
      func = f;
    },
    restore: function(context) {
      func && func(context);
    }
  }
}

现在,getInitialState我正在为组件创建一个新实例:

component: <StatefulView instance={createInstance()}/>

然后在StatefulView我只需要在componentWillMount和 中保存和恢复componentWillUnmount

componentWillMount: function() {
  this.props.instance.restore(this)
},
componentWillUnmount: function() {
  var state = this.state
  this.props.instance.save(function(ctx){
    ctx.setState(state)
  })
}

就是这样。它对我来说非常有效。现在我可以将组件视为实例:)

这是一个很好的修复,谢谢
2021-06-01 23:05:07
这看起来完全像我需要的东西。你能再详细一点吗?举个在现实生活中存储组件状态并恢复的例子?
2021-06-05 23:05:07

对于那些刚刚在 2019 年或以后阅读本文的人,其他答案中已经给出了很多细节,但我想在这里强调几点:

  • 在某些商店 (Redux) 或 Context 中保存状态可能是最好的解决方案。
  • 仅当您的组件一次只有一个实例时,存储在全局变量中才有效。(每个实例如何知道哪个存储状态是他们的?)
  • 保存在 localStorage 与全局变量有相同的警告,而且由于我们不是在谈论在浏览器刷新时恢复状态,它似乎没有增加任何好处。
所有这些都是等效的,并有相同的问题。在 Redux 中存储 state 仍然没有解决实例的问题
2021-06-07 23:05:07
我遇到了同样的情况,我没有去使用持久库,只是因为我想要一个即使用户不同也应该发生的通用模式。只需使用redux
2021-06-18 23:05:07

嗯,您可以使用localStorage, 或者AsyncStorageReact Native。

react网

componentWillUnmount() {
  localStorage.setItem('someSavedState', JSON.stringify(this.state))
}

然后当天晚些时候或 2 秒后:

componentWillMount() {
  const rehydrate = JSON.parse(localStorage.getItem('someSavedState'))
  this.setState(rehydrate)
}

react本机

import { AsyncStorage } from 'react-native'

async componentWillMount() {
  try {
    const result = await AsyncStorage.setItem('someSavedState', JSON.stringify(this.state))
    return result
  } catch (e) {
    return null
  }
}

然后当天晚些时候或 2 秒后:

async componentWillMount() {
  try {
    const data = await AsyncStorage.getItem('someSavedState')
    const rehydrate = JSON.parse(data)
    return this.setState(rehydrate)
  } catch (e) {
    return null
  }
}

您还可以Redux在呈现时使用数据并将其传递给子组件。您可能会从研究serializing状态和 ReduxcreateStore函数的第二个参数中受益,该参数用于恢复初始状态。

请注意,这JSON.stringify()是一项代价高昂的操作,因此您不应在按键等操作上执行此操作。如果您有疑虑,请调查去抖动。

localStorage 是一个有趣的网络概念,可以试一试。谢谢
2021-05-24 23:05:07
我会说性能是在通过卸载使数据持久化之后的次要主要问题。在上游组件中存储数据在 React 中基本上是“提升状态”。这比将数据保存到磁盘更高效,因为您正在更改对内存中数据的引用并且基本上避免垃圾收集,因此读/写速度将是涡轮增压;但是,将数据存储在 localStorage 中允许数据在卸载和重新安装根组件本身时保持不变。
2021-06-09 23:05:07
localStorage 是安全的。您最多可以在其中存储大约 15 MB 的数据,具体取决于浏览器等。它非常适合短期持久数据。
2021-06-21 23:05:07
性能怎么样?与改变 React 应用程序的全局状态相比,读/写本地存储是否需要时间?静音状态可能会重新渲染大量组件,这就是为什么我也在寻找一种解决方案来在卸载/挂载之间保存单个组件状态。但只是以一种高性能的方式。
2021-06-21 23:05:07

我迟到了,但如果你使用的是 Redux。使用 redux-persist 几乎可以立即获得这种行为。只需将其添加autoRehydrate到商店,然后它就会侦听REHYDRATE将自动恢复组件先前状态的操作(从 Web 存储)。