首页 文章

ReactJS状态与道具

提问于
浏览
117

这可能是在回答问题和自以为是之间的那条线,但我会来回顾如何构建ReactJS组件,因为复杂性增加并且可以使用某些方向 .

Coming from AngularJS, I want to pass my model into the component as a property and have the component modify the model directly. Or should I be splitting the model up into various state properties and compiling it back together when sending back upstream? What is the ReactJS way?

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

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 回答

  • 35

    Updating 2016: React已更改,解释"props vs state"变得非常简单 . 如果组件需要更改数据 - 将其置于某个状态,否则将置于道具中 . 因为 props are read-only 现在 .

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

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

    Changing props and state

  • 95

    阅读Thinking in React

    让我们通过每一个,找出哪个是州 . 简单地询问关于每个数据的三个问题:它是通过道具从父母那里传来的吗?如果是这样,它可能不是状态 . 它会随着时间而改变吗?如果没有,它可能不是状态 . 您可以根据组件中的任何其他状态或道具来计算它吗?如果是这样,那就不是状态 .

  • 63

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

    基本上你有两个React组件:

    • a "pure"显示组件,用于处理样式和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时 . 但是,此示例中的基础状态管理逻辑保持不变,更重要的是,保持状态和表示逻辑分离所带来的好处仍然以相同的方式应用 .

  • 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:'bar'}作为值prop . 如果用户单击锚点,则父组件的状态将更新为{value:{foo:'barbar'}},从而触发内部组件的重新呈现过程,该过程将接收{foo:'barbar'} as道具的新 Value .

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

    因此,由于我们会错过道具上的更改并使重新渲染过程短路,因此UI不会从“bar”更新为“barbar”

  • 16

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

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

    修改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} />;
      }
    });
    
  • 11

    来自React doc

    props是不可变的:它们从父级传递,并由父级“拥有” . 为了实现交互,我们向组件引入了可变状态 . this.state是组件的私有,可以通过调用this.setState()来更改 . 更新状态后,组件将重新呈现自身 .

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

相关问题