我在看Pluralsight关于React的课程,老师说道具不应该被改变。我现在正在读一篇关于道具vs.国家的文章(uberVU/react-guide),它说

道具和状态更改都会触发呈现更新。

文章后面说:

Props(属性的缩写)是组件的配置,如果可以的话,是它的选项。它们是从上面接收的,是不可变的。

所以道具可以改变,但它们应该是不可变的? 什么时候应该使用道具,什么时候应该使用状态? 如果你有一个React组件需要的数据,它应该通过道具或设置在React组件通过getInitialState?


当前回答

基本上,区别在于状态类似于OOP中的属性:它是类(组件)的局部属性,用于更好地描述它。道具就像参数一样——它们从组件的调用者(父组件)传递给组件:就像你调用一个带有特定参数的函数一样。

其他回答

大多数时候,你的子组件是无状态的,这意味着它们代表了它的父组件给它的数据/信息。

另一方面,状态处理的是处理组件本身,状态可以在组件内部通过setState和useState钩子改变。

例如

class Parent extends Component{
  constructor(props){
    super(props);
    this.state = {
      fruit: 'apple'
    }
    this.handleChange = this.handleChange.bind(this)
  }

  handleChange(){
    this.setState({fruit: 'mango'})
  }
  render(){
    return (
      <div>
      <Child  fruit={this.state.fruit} />
      <button onClick={this.handleChange}>Change state</button>
      </div>
    )
  }
}

当点击按钮时,父类将其状态从apple更改为mango,并将其状态作为道具传递给子组件。现在,没有状态的子组件根据父组件的状态显示不同的标题。

class Child extends Component{

  render(){
    return(
      <h1>I have received a prop {this.props.fruit}</h1>
    )
  }
}

所以在根级别上,道具是父进程与子进程的通信,而状态是描述父进程的情况等。

在react中,我们通常使用模块来分离组件。有时分离的组件需要在它们内部共享数据。如果父组件有一些数据应该在子组件上共享,那么道具是解决方案。我们可以使用从父母到孩子的道具共享数据。Props是不可变的,这意味着您不能更改数据。

另一方面,状态用于跟踪数据中的任何变化。您可以将状态视为数据的存储和可以更新该数据的函数。状态是可变的。您可以在同一组件中使用setState函数更改状态

简而言之。

道具值不能改变[不可变] 使用setState方法[mutable]可以改变状态值

Basically, props and state are two ways the component can know what and how to render. Which part of the application state belongs to state and which to some top-level store, is more related to your app design, than to how React works. The simplest way to decide, IMO, is to think, whether this particular piece of data is useful for application as a whole, or it's some local information. Also, it's important to not duplicate state, so if some piece of data can be calculated from props - it should calculated from props.

For example, let's say you have some dropdown control (which wraps standart HTML select for custom styling), which can a) select some value from list, and b) be opened or closed (i.e., the options list displayed or hidden). Now, let's say your app displays a list of items of some sort and your dropdown controls filter for list entries. Then, it would be best to pass active filter value as a prop, and keep opened/closed state local. Also, to make it functional, you would pass an onChange handler from parent component, which would be called inside dropdown element and send updated information (new selected filter) to the store immediately. On the other hand, opened/closed state can be kept inside dropdown component, because the rest of the application doesn't really care if the control is opened, until user actually changes it value.

下面的代码是不完全工作,它需要css和处理下拉单击/模糊/改变事件,但我想保持示例最小。希望这有助于理解其中的区别。

const _store = {
    items: [
    { id: 1, label: 'One' },
    { id: 2, label: 'Two' },
    { id: 3, label: 'Three', new: true },
    { id: 4, label: 'Four', new: true },
    { id: 5, label: 'Five', important: true },
    { id: 6, label: 'Six' },
    { id: 7, label: 'Seven', important: true },
    ],
  activeFilter: 'important',
  possibleFilters: [
    { key: 'all', label: 'All' },
    { key: 'new', label: 'New' },
    { key: 'important', label: 'Important' }
  ]
}

function getFilteredItems(items, filter) {
    switch (filter) {
    case 'all':
        return items;

    case 'new':
        return items.filter(function(item) { return Boolean(item.new); });

    case 'important':
        return items.filter(function(item) { return Boolean(item.important); });

    default:
        return items;
  }
}

const App = React.createClass({
  render: function() {
    return (
            <div>
            My list:

            <ItemList   items={this.props.listItems} />
          <div>
            <Dropdown 
              onFilterChange={function(e) {
                _store.activeFilter = e.currentTarget.value;
                console.log(_store); // in real life, some action would be dispatched here
              }}
              filterOptions={this.props.filterOptions}
              value={this.props.activeFilter}
              />
          </div>
        </div>
      );
  }
});

const ItemList = React.createClass({
  render: function() {
    return (
      <div>
        {this.props.items.map(function(item) {
          return <div key={item.id}>{item.id}: {item.label}</div>;
        })}
      </div>
    );
  }
});

const Dropdown = React.createClass({
    getInitialState: function() {
    return {
        isOpen: false
    };
  },

  render: function() {
    return (
        <div>
            <select 
            className="hidden-select" 
          onChange={this.props.onFilterChange}
          value={this.props.value}>
            {this.props.filterOptions.map(function(option) {
            return <option value={option.key} key={option.key}>{option.label}</option>
          })}
        </select>

        <div className={'custom-select' + (this.state.isOpen ? ' open' : '')} onClick={this.onClick}>
            <div className="selected-value">{this.props.activeFilter}</div>
          {this.props.filterOptions.map(function(option) {
            return <div data-value={option.key} key={option.key}>{option.label}</div>
          })}
        </div>
      </div>
    );
  },

  onClick: function(e) {
    this.setState({
        isOpen: !this.state.isOpen
    });
  }
});

ReactDOM.render(
  <App 
    listItems={getFilteredItems(_store.items, _store.activeFilter)} 
    filterOptions={_store.possibleFilters}
    activeFilter={_store.activeFilter}
    />,
  document.getElementById('root')
);

道具只是属性的简写。道具是组件之间相互交流的方式。如果你熟悉React,那么你应该知道道具是从父组件向下流动的。

还有一种情况是,你可以有默认的道具,这样即使父组件没有传递道具,道具也会被设置。

这就是为什么人们认为React具有单向数据流。这需要一些理解,我可能会在后面的博客中对此进行讨论,但现在只要记住:数据从父流向子。道具是不可变的(是指不变的)

所以我们很高兴。组件从父组件接收数据。都整理好了,对吧?

嗯,不完全是。当组件从父组件以外的对象接收数据时会发生什么?如果用户直接向组件输入数据呢?

这就是我们有状态的原因。

状态

道具不应该改变,所以状态上升。组件通常没有状态,因此被称为无状态。使用状态的组件称为有状态的。在聚会上随便说说你的小秘密,然后看着人们慢慢地离你而去。

所以使用状态是为了让组件可以在它所做的任何渲染之间跟踪信息。当你setState时,它更新状态对象,然后重新渲染组件。这非常酷,因为这意味着React会处理困难的工作,并且速度非常快。

作为状态的一个小例子,这里是一个搜索栏的片段(如果你想了解更多关于React的知识,值得看看这门课程)

Class SearchBar extends Component {
 constructor(props) {
  super(props);
this.state = { term: '' };
 }
render() {
  return (
   <div className="search-bar">
   <input 
   value={this.state.term}
   onChange={event => this.onInputChange(event.target.value)} />
   </div>
   );
 }
onInputChange(term) {
  this.setState({term});
  this.props.onSearchTermChange(term);
 }
}

总结

道具和状态的作用类似,但使用方式不同。大多数组件可能是无状态的。

道具用于将数据从父组件传递给子组件或由组件本身传递。它们是不可变的,因此不会被改变。

State用于可变数据或将更改的数据。这对于用户输入特别有用。以搜索栏为例。用户将输入数据,这将更新他们所看到的内容。