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

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

文章后面说:

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

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


当前回答

react中的state和props都用于将数据控制到组件中,通常props由父组件设置并传递给子组件,并且它们在整个组件中都是固定的。对于将要改变的数据,我们必须使用状态。道具是不可变的,而状态是可变的,如果你想改变道具,你可以从父组件做,然后传递给子组件。

其他回答

道具和状态是相关的。一个组件的状态通常会成为子组件的道具。道具在父元素的呈现方法中作为React.createElement()的第二个参数传递给子元素,如果您使用的是JSX,则是更熟悉的标记属性。

<MyChild name={this.state.childsName} />

父类的childsName的状态值变成子类的this.props.name。从子进程的角度来看,名称道具是不可变的。如果它需要改变,父进程只需要改变它的内部状态:

this.setState({ childsName: 'New name' });

React会把它传播给你的子程序。一个自然的后续问题是:如果子程序需要更改其名称道具怎么办?这通常是通过子事件和父回调完成的。子进程可能会公开一个名为onNameChanged的事件。然后父进程通过传递回调处理程序来订阅事件。

<MyChild name={this.state.childsName} onNameChanged={this.handleName} />

子进程将通过调用this.props将其请求的新名称作为参数传递给事件回调。onNameChanged('New name'),父进程将在事件处理程序中使用该名称来更新其状态。

handleName: function(newName) {
   this.setState({ childsName: newName });
}

Props: Props只是组件的属性,react组件只是一个javascript函数。

  class Welcome extends React.Component {
    render() {
      return <h1>Hello {this.props.name}</h1>;
    }
  }

Const元素=;

这里<Welcome name="Sara" />传递一个对象{name: 'Sara'}作为Welcome组件的道具。为了将数据从一个父组件传递给子组件,我们使用props。 道具是不可变的。在组件的生命周期中,道具不应该改变(认为它们是不可变的)。

状态:状态只能在组件中访问。为了跟踪组件中的数据,我们使用状态。我们可以通过setState来改变状态。如果我们需要将state传递给child,我们必须将它作为props传递。

class Button extends React.Component {
  constructor() {
    super();
    this.state = {
      count: 0,
    };
  }

  updateCount() {
    this.setState((prevState, props) => {
      return { count: prevState.count + 1 }
    });
  }

  render() {
    return (<button
              onClick={() => this.updateCount()}
            >
              Clicked {this.state.count} times
            </button>);
  }
}

摘自:Andrea Chiarelli的书《开始React:用React简化前端开发工作流,增强应用程序的用户体验》:

Every React component has a props property. The purpose of this property is to collect data input passed to the component itself. JSX attribute is attached to a React element, a property with the same name is attached to the props object. So, we can access the passed data by using the attached property. In addition, the immutability of props allows us to think of components as pure functions, which are functions that have no side effects (since they don't change their input data). We can think of data passing from one component to another as a unidirectional data flow, from the parent component toward the child components. This gives us a more controllable system.

React provides a mechanism to support the automatic rendering of a component when data changes. Such a mechanism is based on the concept of state. React state is a property that represents data that changes over time. Every component supports the state property, but it should be used carefully. Components that store data that can change over time are said to be stateful components. A stateful component stores the state in the this.state property. To inform a component that the state has changed, you must use the setState() method. State initialization is the only case where you can assign a value to the this.state property without using setState().

setState()将新数据与已包含在状态中的旧数据合并,并覆盖先前的状态 setState()会触发render()方法的执行,所以永远不要显式地调用render()

主要的区别是状态只能在组件内部处理,而道具则在组件外部处理。如果我们从外部获取数据并进行处理,在这种情况下,我们应该使用状态。

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')
);