【问题标题】:ReactJs: change state in response to state changeReactJs:改变状态以响应状态变化
【发布时间】:2014-11-06 01:45:03
【问题描述】:

我有一个带有输入的 React 组件和一个可选的“高级输入”:

[ basic ]
Hide Advanced...
[ advanced ]

如果您单击“隐藏高级”,底部的高级将消失,变为“显示高级”。这很简单并且工作正常,状态中有一个showAdvanced 键,用于控制文本以及是否呈现高级输入。

但是,外部 JS 代码可能会更改高级的值,在这种情况下,如果 [高级] 输入当前处于隐藏状态并且该值与默认值不同,我想显示它。但是,用户应该能够单击“隐藏高级”再次将其关闭。

所以,有人外部呼叫cmp.setState({advanced: "20"}),然后我想显示高级;最直接的做法就是在我的状态下更新showAdvanced。但是,似乎没有办法更新某些状态以响应 React 中的其他状态更改。我可以想到一些行为略有不同的解决方法,但我真的很想拥有这种特定的行为。

我应该将 showAdvanced 移至道具,这有意义吗?你能改变 props 以响应状态变化吗?谢谢。

【问题讨论】:

  • 根据另一种状态设置一个状态听起来像是一种反模式。为什么不直接检查 render 方法中的advanced 状态?
  • 你有 jsfiddle 吗?如果我理解正确,您想从组件内部控制元素的可见性,但也从外部控制?所以我只会使用一个道具,并有一个 if 条件来检查你是否应该显示它。
  • @DavidHellsing 我有同样的问题并且不认为它是反模式:考虑如果响应其他状态变化的状态变化需要从网络或复杂计算中检索数据的情况。相反,每次渲染运行时都检索它是一种反模式!

标签: javascript state reactjs


【解决方案1】:

在下面的评论中修改答案。


我最初的错误答案:

生命周期函数componentWillUpdate 将在收到新状态或道具时运行。您可以在此处找到相关文档:http://facebook.github.io/react/docs/component-specs.html#updating-componentwillupdate

如果在调用外部setState 时,然后将componentWillUpdate 中的showAdvanced 设置为true,您应该会得到想要的结果。

编辑:另一种选择是将setState 的外部调用包括showAdvanced: true 的新状态。

【讨论】:

  • 文档说你不能在 ComponentWillUpdate 中使用 setState,这就是我一开始就有问题的原因。我曾想过让外部 JS 处理状态,但这会将特定于组件的逻辑推给不应该处理它的消费者。
  • 啊,原来如此。您可能希望通过props 设置showAdvanced 的初始值。与advanced_text 相同。然后,如果另一个组件设置advanced_text 状态,您将在您的render 中使用这样的逻辑:this.state.advanced_text || this.props.initial_advanced_text。如果设置了this.state.advanced_text,您还可以隐藏Hide Advanced 按钮。
【解决方案2】:

好的,首先,您提到您的组件之外的第三方可能会调用cmp.setState()?这是一个巨大的反应,不。一个组件应该只调用它自己的 setState 函数——外部的任何东西都不能访问它。

另外要记住的是,如果您尝试再次更改状态以响应状态更改 - 这意味着您做错了什么。

当您以这种方式构建事物时,它会使您的问题变得比实际需要的困难得多。原因是如果您接受没有任何外部可以设置组件的状态 - 那么基本上您唯一的选择就是允许外部事物更新组件的道具 - 然后在组件内部对它们做出反应。这简化了问题。

因此,例如,您应该考虑让以前调用 cmp.setState() 的任何外部事物再次在您的组件上调用 React.renderComponent,并提供新的 prop 或 prop 值,例如将 showAdvanced 设置为 true .然后,您的组件可以在 componentWillReceiveProps 中对此做出反应并相应地设置其状态。下面是一段代码示例:

var MyComponent = React.createClass({
    getInitialState: function() {
        return {
            showAdvanced: this.props.showAdvanced || false
        }
    },
    componentWillReceiveProps: function(nextProps) {
        if (typeof nextProps.showAdvanced === 'boolean') {
            this.setState({
                showAdvanced: nextProps.showAdvanced
            })
        }
    },
    toggleAdvancedClickHandler: function(e) {
        this.setState({
            showAdvanced: !this.state.showAdvanced
        })
    },
    render: function() {
        return (
            <div>
                <div>Basic stuff</div>
                <div>
                    <button onClick={this.toggleAdvancedClickHandler}>
                        {(this.state.showAdvanced ? 'Hide' : 'Show') + ' Advanced'}
                    </button>
                </div>
                <div style={{display: this.state.showAdvanced ? 'block' : 'none'}}>
                    Advanced Stuff
                </div>
            </div>
        );
    }
});

所以当你第一次调用React.renderComponent(MyComponent({}), elem) 时,组件将被挂载,高级 div 将被隐藏。如果单击组件内的按钮,它将切换并显示。如果您需要强制组件从组件外部显示高级 div,只需再次调用 render ,如下所示:React.renderComponent(MyComponent({showAdvanced: true}), elem) 它将显示它,而不管内部状态如何。同样,如果您想从外部隐藏它,只需使用 showAdvanced: false 调用它。

对上述代码示例的额外好处是,在 componentWillReceiveProps 内部调用 setState 不会导致另一个渲染周期,因为它会在调用 render 之前捕获并更改状态。请查看此处的文档以获取更多信息:http://facebook.github.io/react/docs/component-specs.html#updating-componentwillreceiveprops

不要忘记在已经挂载的组件上再次调用 renderComponent 并不会再次挂载它,它只是告诉 react 更新组件的 props,然后 react 将进行更改、运行生命周期和渲染函数组件并做它的 dom diffing 魔术。

【讨论】:

  • 谢谢,这绝对是正确的答案。我遇到了一些额外的问题(外部组件设置和读取的值实际上是序列化/反序列化以分解高级/基本部分),但是通过道具而不是从外部调用 setState 来解决我的问题。
  • 在子级上设置 React 组件的状态和向子级传递设置状态的回调之间有真正的区别吗?为什么第一个是“巨大的禁忌”而第二个被推荐并在任何地方使用(例如:medium.com/@ruthmpardee/…、material-ui.com/demos/dialogs)?
  • 迈克这真是太有见地了。所以只是为了确保我理解你的意思是 React 组件不应该直接改变另一个组件的状态?理想情况下,props 应该在状态更改时传递给嵌套组件。但是,当状态需要在多个地方更改时,我不确定该怎么做。例如,如果有一个&lt;Input /&gt; 组件,我可能需要将该值传递到&lt;Form /&gt; 的状态,以便在提交之前对所有值进行分组。也许我很困惑,但真正解决这些问题对我来说是一个挑战。谢谢
  • 我有同样的问题并且不认为它是反模式:考虑如果响应其他状态变化的状态变化需要从网络或复杂计算中检索数据的情况。相反,每次渲染运行时都检索它是一种反模式!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-25
  • 1970-01-01
相关资源
最近更新 更多