ReactJS状态与道具

时间:2014-05-05 20:18:24

标签: reactjs

这可能是在回答问题和自以为是之间的界限,但我会反复思考如何构建ReactJS组件,因为复杂性增加并且可以使用某些方向。

来自AngularJS,我希望将我的模型作为属性传递给组件,并让组件直接修改模型。或者我应该将模型拆分为各种state属性,并在向上游发送时将其重新编译回来?什么是ReactJS方式?

以博客文章编辑器为例。试图直接修改模型最终看起来像:

var PostEditor = React.createClass({
  updateText: function(e) {
    var text = e.target.value;
    this.props.post.text = text;
    this.forceUpdate();
  },
  render: function() {
    return (
      <input value={this.props.post.text} onChange={this.updateText}/>
      <button onClick={this.props.post.save}/>Save</button>
    );
  }
});

这似乎不对。

更多是React方式来制作我们的text模型属性state,并在保存之前将其编译回模型:

var PostEditor = React.createClass({
  getInitialState: function() {
    return {
      text: ""
    };
  },
  componentWillMount: function() {
    this.setState({
      text: this.props.post.text
    });
  },
  updateText: function(e) {
    this.setState({
      text: e.target.value
    });
  },
  savePost: function() {
    this.props.post.text = this.state.text;
    this.props.post.save();
  },
  render: function() {
    return (
      <input value={this.state.text} onChange={this.updateText}/>
      <button onClick={this.savePost}/>Save</button>
    );
  }
});

这并不需要调用this.forceUpdate(),但随着模型的增长,(帖子可能包含作者,主题,标签,评论,评级等等),该组件开始获取真的很复杂。

第一种方法是ReactLink的方法吗?

6 个答案:

答案 0 :(得分:95)

正在更新2016: React被改变了,解释&#34; props vs state&#34;变得非常简单。如果组件需要更改数据 - 将其置于某个状态,否则将置于道具中。因为道具现在是只读的

道具和州之间的确切区别是什么?

你可以找到很好的解释here(完整版)

Changing props and state

答案 1 :(得分:63)

你的第二种方法更像是它。 React并不关心模型,因为它关心以及它们如何在您的应用程序中流动。理想情况下,您的帖子模型将存储在根目录中的单个组件中。然后创建子组件,每个组件都使用模型的一部分。

您可以将回调传递给需要修改数据的子项,并从子组件中调用它们。

直接修改this.props或this.state不是一个好主意,因为React将无法接收更改。这是因为React对你的后道具进行浅层比较,以确定它是否已经改变。

我制作了这个jsfiddle来展示数据如何从外部组件流向内部组件:

http://jsfiddle.net/jxg/M3CLB/

handleClick方法显示了3种方式(im)正确更新状态:

var Outer = React.createClass({

  getInitialState: function() {
    return {data: {value: 'at first, it works'}};
  },

  handleClick: function () {

    // 1. This doesn't work, render is not triggered.
    // Never set state directly because the updated values
    // can still be read, which can lead to unexpected behavior.

    this.state.data.value = 'but React will never know!';

    // 2. This works, because we use setState

    var newData = {value: 'it works 2'};
    this.setState({data: newData});

    // 3. Alternatively you can use React's immutability helpers
    // to update more complex models.
    // Read more: http://facebook.github.io/react/docs/update.html

    var newState = React.addons.update(this.state, {
      data: {value: {$set: 'it works'}}
    });
    this.setState(newState);
 },

  render: function() {
      return <Inner data={this.state.data} handleClick={this.handleClick} />;
  }
});

答案 2 :(得分:35)

来自React doc

  

道具是不可变的:它们是从父母那里传来的,并且是“拥有的”#34;由父母。为了实现交互,我们向组件引入了可变状态。 this.state是组件的私有,可以通过调用this.setState()来更改。更新状态后,组件将重新呈现自己。

TrySpace :当更新道具(或状态)时(通过setProps / setState或parent),组件也会重新渲染。

答案 3 :(得分:16)

来自Thinking in React的阅读:

  

让我们通过每一个,找出哪一个是州。只是   询问关于每个数据的三个问题:

     
      
  1. 是通过道具从父母那里传来的吗?如果是这样,可能不是   州。
  2.   
  3. 它会随着时间的推移而改变吗?如果没有,它可能不是州。

  4.   
  5. 你能根据你的任何其他状态或道具来计算吗?       零件?如果是这样,那就不是状态了。

  6.   

答案 4 :(得分:11)

我不确定我是否在回答您的问题,但我发现,特别是在大型/不断发展的应用程序中,容器/组件模式运行得非常好。

基本上你有两个React组件:

  • “纯”显示组件,用于处理样式和DOM交互;
  • 一个容器组件,用于处理访问/保存外部数据,管理状态和呈现显示组件。

实施例

N.B。这个例子可能过于简单,无法说明这种模式的好处,因为对于这种直截了当的案例来说,它非常冗长。

/**
 * Container Component
 *
 *  - Manages component state
 *  - Does plumbing of data fetching/saving
 */

var PostEditorContainer = React.createClass({
  getInitialState: function() {
    return {
      text: ""
    };
  },

  componentWillMount: function() {
    this.setState({
      text: getPostText()
    });
  },

  updateText: function(text) {
    this.setState({
      text: text
    });
  },

  savePost: function() {
    savePostText(this.state.text);
  },

  render: function() {
    return (
      <PostEditor
        text={this.state.text}
        onChange={this.updateText.bind(this)}
        onSave={this.savePost.bind(this)}
      />
    );
  }
});


/**
 * Pure Display Component
 *
 *  - Calculates styling based on passed properties
 *  - Often just a render method
 *  - Uses methods passed in from container to announce changes
 */

var PostEditor = React.createClass({
  render: function() {
    return (
      <div>
        <input type="text" value={this.props.text} onChange={this.props.onChange} />
        <button type="button" onClick={this.props.onSave} />
      </div>
    );
  }
});

优势

通过将显示逻辑和数据/状态管理分开,您有一个可重复使用的显示组件:

  • 可以使用react-component-playground
  • 之类的东西轻松地使用不同的道具集进行迭代
  • 可以使用不同的容器包装以实现不同的行为(或与其他组件结合使用以构建应用程序的更大部分

您还有一个处理所有外部通信的容器组件。如果您稍后在*上进行任何重大更改,这将使您更容易灵活地访问数据。

这种模式也使编写和实现单元测试变得更加直接。

迭代了一个大的React应用程序几次后,我发现这种模式让事情变得相对轻松,特别是当你有更大的组件具有计算样式或复杂的DOM交互时。

*阅读助焊剂模式,并查看 Marty.js,这很大程度上激发了这个答案(我最近一直在使用很多) Redux(和react-redux),非常好地实现了这种模式。

  

请注意2018年或之后阅读此内容的人:

     

自从写完这个答案以来,React已经发展了很多,特别是在引入Hooks之后。但是,此示例中的基础状态管理逻辑保持不变,更重要的是,保持状态和表示逻辑分离所带来的好处仍然以相同的方式应用。

答案 5 :(得分:0)

我认为您正在使用Facebook已经在此处解释的反模式link

这是你发现的事情:

React.createClass({
  getInitialState: function() {
    return { value: { foo: 'bar' } };
  },

  onClick: function() {
    var value = this.state.value;
    value.foo += 'bar'; // ANTI-PATTERN!
    this.setState({ value: value });
  },

  render: function() {
    return (
      <div>
        <InnerComponent value={this.state.value} />
        <a onClick={this.onClick}>Click me</a>
      </div>
    );
  }
});

第一次渲染内部组件时,它会有{foo:&#39; bar&#39;作为价值道具。如果用户点击锚点,则父组件的状态将更新为{value:{foo:&#39; barbar&#39;触发内部组件的重新呈现过程,它将接收{foo:&#39; barbar&#39; }作为道具的新价值。

问题是,由于父组件和内组件共享对同一对象的引用,当对象在onClick函数的第2行上变异时,内部组件所具有的prop将会改变。因此,当重新呈现过程开始并调用shouldComponentUpdate时,this.props.value.foo将等于nextProps.value.foo,因为实际上this.props.value引用与nextProps.value相同的对象。

因此,由于我们错过了道具上的更改并使重新渲染过程短路,因此UI无法从“条形码”中更新。到&#39; barbar&#39;