【问题标题】:React + Flux - should store data be stored in a component state, or props?React + Flux - 存储数据应该存储在组件状态还是道具中?
【发布时间】:2014-10-16 18:07:02
【问题描述】:

如果通量存储是维护数据状态的单例,为什么组件在访问存储时使用 setState 而不是 setProps?这不只是意味着我开始将应用程序状态保存在两个(或更多)地方吗?

Flux / React 文档和示例似乎都将 setState 视为首选解决方案,但我与工作中的几位同事进行了一次有趣的对话,想知道是否有其他人遇到过这个

编辑: 你可以在这个网址中看到我在说什么: https://github.com/facebook/flux/blob/master/examples/flux-chat/js/components/ThreadSection.react.js

注意 ThreadSection 是一个子组件,它直接从存储中获取数据并将其用作状态。

如果您遵循 React “方式”,我会期望状态由商店管理 - 而不是子组件。

我们想到的解决方案是获取顶层组件中的所有存储(作为道具),并根据需要将它们传递给子组件。但这很快就会变得相当丑陋。

我们这样做是因为 setProps 不适用于子组件

【问题讨论】:

  • 你能粘贴一些你提到的代码吗?

标签: reactjs reactjs-flux refluxjs


【解决方案1】:

此问题的有效答案隐藏在先前答案的 cmets 中:

@idolize 你也可以使用 React 上下文(一个隐藏的, 尚未正式记录的功能)。这真的很好,因为你 不必做所有传递层次结构的事情。有几个 关于上下文的文章,在线搜索! – 安迪 2015 年 7 月 17 日在 18:41

【讨论】:

    【解决方案2】:

    根据正式文档,商店应该更新父组件的状态,并通过他的孩子道具传递它:

    当它从 store 接收到 event 时,它首先通过 store 的 public getter 方法请求它需要的新数据。然后它调用它自己的 setState() 或 forceUpdate() 方法,导致它的 render() 方法和它所有后代的 render() 方法运行。

    我们经常将 store 的整个状态传递到单个对象中的视图链中,允许不同的后代使用他们需要的东西。除了将类似于控制器的行为保持在层次结构的顶部,从而使我们的后代视图在功能上尽可能纯,将 store 的整个状态传递到单个对象中还具有减少 props 数量的效果我们需要管理。

    (facebook flux docs - Overview)

    【讨论】:

    • 嗨,困扰我的是他们提供的替代方案 (forceUpdate()),我同意这听起来不像他们建议的默认值,但它是一个有效的选项,这意味着我可以传入 props 并且只是用户 forceUpdate().
    • 使用 forceUpdate 是为了直接使用 store 数据,而不是通过 props 或 states
    • @yarden 如果 store 是通过 prop 传入而不是通过单例直接引用,则它可以与 props 一起使用。我实际上更喜欢以这种方式传递存储(依赖注入模式),因为它允许在不同的情况下使用不同的存储,例如服务器端渲染(我们可能不想拉入所有客户端调度程序/动作/常量/etc 的东西)。
    • 到目前为止,我还没有发现在调度程序的状态更改回调中使用 setState(newState)forceUpdate() 的真正区别。我猜理论上该状态可能与setState 不同步,但我从未发生过这种情况,而且我认为它看起来更干净一些。就像我说的,任何一种方式似乎都同样有效。
    • @idolize 你还可以使用 React 上下文传递存储(一个隐藏的,尚未正式记录的功能)。这真的很好,因为你不必做所有传递层次结构的事情。有几篇关于上下文的文章,网上搜索一下!
    【解决方案3】:

    将 store 数据放在组件的 state 中更有意义,这是因为 props 可能会被带有 componentWillReceiveProps 的父组件更改。因此,随时更新state 是有意义的:

    • 商店的更改事件被触发并且
    • 每当道具发生变化时(将仅与组件本身相关的衍生数据放入状态)

    下面是一个示例组件,它更新侦听reflux store 以及道具更改。我很少在render 函数中使用this.props,而是在新道具出现时修改它们(创建仅在组件本身内使用的派生数据)。我经常遇到这种模式,所以不妨写下来:

    var SampleComponent = React.createClass({
        mixins: [Reflux.ListenerMixin],
    
        // reusable helper function to build state object
        buildStateFromProps: function(props) {
            return {
                actualHeight: props.height + 20
            }
        },
    
        // default props if no such was set by a parent component
        getDefaultProps: function() {
            return {
                height: 100
            };
        },
    
        // initial state with all value set to something default
        // even using buildStateFromProps with default props
        getInitialState: function() {
            // this.props is built before this.state
            var state = buildStateFromProps(this.props);
            // append default data from store
            state.text = '';
        },
    
        // happens when the parent component send different 
        // props data
        componentWillReceiveProps: function(nextProps) {
            // building derivative data from new props
            // reusing buildStateFromProps
            this.setState(buildStateFromProps(nextProps));
        },
    
        // setting up store to be used by the component
        componentDidMount: function() {
            // this.listenTo is a helper function ListenerMixin
            this.listenTo(sampleStore, sampleUpdated);
        },
    
        // is called from the sampleStore update
        sampleUpdated: function(sampleData) {
            this.setState({
                text: sampleData.text
            });
        },
    
        render: function() {
            return (
                // ... 
                // using this.state.text from store updates and
                // this.state.height from prop updates
            );
        }
    });
    

    我将 props 数据发送到 state 的原因是为了避免渲染函数混乱。否则,渲染函数将包含大量与“渲染”组件无关的代码。此外,如果这些派生数据用于应用程序的其他部分,那么很容易将其从组件中拉出并放入存储中。

    希望这会有所帮助。

    【讨论】:

      【解决方案4】:

      了解您应该有 2 种组件。有状态组件和视图组件。

      有状态组件可以有 3 种状态:初始状态、用户输入状态和数据存储状态。

      有状态组件就像您正在组装的“小部件”中的小入口点。下游依赖或数据注入不再有单个应用程序范围的入口点,因为所有这些小部件都有自己独立的生命周期。这就是他们自己需要访问和收听商店的原因。

      除了行为属性,有状态组件不通过上游属性接收实际数据。

      有状态组件管理自己的状态并将其传递给其子级以通过下​​游属性进行渲染。

      有状态组件通常不会直接渲染 html DOM 元素本身。它们更像 MVC 中的控制器,并使用其他更笨的组件(如 MVC 中的视图)来实际渲染 DOM 元素。

      Dumber 组件类似于视图,因此它们只包含渲染 DOM 元素的逻辑。将它们视为仅接收属性的 handlebars.js 模板,并简单地将它们渲染为可能带有循环等的 DOM 元素。它们是无状态渲染器。

      希望这能回答你的问题。

      【讨论】:

        猜你喜欢
        • 2017-12-19
        • 1970-01-01
        • 2017-02-23
        • 2021-02-22
        • 1970-01-01
        • 1970-01-01
        • 2015-02-03
        • 2017-06-14
        • 2016-05-24
        相关资源
        最近更新 更多