25

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

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

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

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

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

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

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

4

4 回答 4

43

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

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

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

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

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

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

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

希望这能回答你的问题。

于 2014-08-24T00:17:10.067 回答
7

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

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

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

(facebook 通量文档 - 概述)

于 2014-08-23T21:54:48.153 回答
5

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

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

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

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 的原因是为了避免弄乱渲染函数。否则,渲染函数将包含大量与“渲染”组件无关的代码。此外,如果此衍生数据用于应用程序的其他部分,则很容易将其从组件中拉出并放入存储中。

希望这可以帮助。

于 2014-08-25T08:35:18.770 回答
1

此问题的有效答案隐藏在对先前答案的评论中:

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

于 2016-02-06T21:29:01.070 回答