React中state和props的区别是什么?

775
我正在观看一个关于React的Pluralsight课程,讲师表示props不应该被更改。现在我正在阅读一篇有关props与state的文章(uberVU/react-guide),它说:

无论是props还是state的变化都会触发重新渲染。

文章后面提到:

Props(属性)是组件的配置,也可以说是选项。它们从上层组件接收并且是不可变的。

  • 那么props可以改变,但是它们应该是不可变的吗?
  • 什么时候应该使用props,什么时候应该使用state?
  • 如果一个React组件需要数据,应该通过props传递还是在React组件中设置getInitialState?

1
https://www.sitepoint.com/work-with-and-manipulate-state-in-react/ React中的状态是组件数据的核心。本文将介绍如何使用和操作状态,以及如何在React应用程序中管理状态。https://appendto.com/2016/05/what-is-difference-between-props-and-state/在React中,props和state都是用于存储组件数据的机制。本文将解释它们之间的区别,并提供一些最佳实践来帮助您更好地理解它们。 - zloctb
19
这是一个非常好的问题,实际上,似乎没有人给出一个简单的答案:/ - Thomas Decaux
属性是通过组件属性传递的,它们不是响应式的。状态是变量,当值发生变化时,React会对其进行响应并更新UI。 - Jone Polvora
我认为“props不应该被更改”的意思是它们不应该被子组件更改。传递props给子组件的父组件可以(并且很可能会)更改它们。 - Rui Marques
50个回答

841

Props和state是相关的。一个组件的状态通常会成为其子组件的props。Props通过父组件的render方法中作为React.createElement()的第二个参数,或者如果您正在使用JSX,则作为更熟悉的标记属性传递给子组件。

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

父组件中的状态值 childsName 将变为子组件的 this.props.name。从子组件的角度来看,名称属性是不可变的。如果需要更改它,父组件应该只更改其内部状态:

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

而 React 会为您传播它到子级。一个自然的后续问题是:如果子级需要更改其 name 属性怎么办?通常可以通过子事件和父回调来完成这个任务。例如,子组件可能公开一个名为 onNameChanged 的事件。然后,父组件将通过传递回调处理程序来订阅该事件。

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

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

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

4
谢谢!所以还有几个问题:1. 为什么有人说 props 不应该改变?2. 数据引导放在哪里?是在组件的初始化中(例如 getInitialState),还是将其放在组件外部,并在数据可用时渲染组件?答案:
  1. 为什么有人说 props 不应该改变? props 通常是由父组件传递给子组件的,如果子组件修改了 props,可能会影响到其他组件或应用程序的状态。同时,props 应该被视为只读数据,因为它们是从上游组件传递下来的。因此,建议不要在组件内部修改 props。
  2. 数据引导放在哪里? 可以在组件的初始化函数中(如 getInitialState)设置默认的数据值,也可以在组件外部定义并在数据可用时再渲染组件。根据具体情况而定,如果数据需要在组件之间共享,则可能更适合将数据定义在组件外部。
- Animal Rights
50
  1. 这是 React 的“功能”方面。所有数据(几乎)向下流动。由于属性被父组件拥有,只有父组件应该更改它。理想情况下,子组件应该是无状态的。在实践中这是不可能的(请参见 React 网站上的表单文档)。
  2. 您可以将其直接提供给顶层组件,这是推荐的做法,也可以将其存储在单独的对象中。其中一种流行的方法是 Flux,它使用称为 Stores 的单例对象。这是更大架构模式的一部分。它也是来自 Facebook 的开源项目,并且与 React 设计一起使用。
- Todd
4
所以,商店就像大型全局变量吗? - SuperUberDuper
7
是的,Flux存储是客户端缓存。还有其他模式,例如最近由Facebook发布的Relay和Redux。 - Todd
21
简而言之,state 是组件内部管理的数据,props 是从上层管理并传递给组件的数据。 - Mark
显示剩余6条评论

273

对于家长与孩子之间的沟通,只需传递props即可。

使用state在控制器视图中存储当前页面所需的数据。

使用props将数据和事件处理程序传递给子组件。

这些列表应该有助于指导您在组件中处理数据时的工作。

Props

  • 是不可变的
    • 这使得React可以进行快速引用检查
  • 用于从视图控制器(您的顶级组件)向下传递数据
  • 具有更好的性能
    • 使用此方法将数据传递给子组件

State

  • 应该由视图控制器(您的顶级组件)管理
  • 是可变的
  • 性能较差
  • 不应从子组件中访问
    • 请改用props将其传递下去
针对没有父子关系的两个组件之间的通信,您可以设置自己的全局事件系统。在componentDidMount()中订阅事件,在componentWillUnmount()中取消订阅,并在接收到事件时调用setState()。Flux模式是其中一种可能的排列方式。- https://facebook.github.io/react/tips/communicate-between-components.html 哪些组件应该拥有状态?大多数组件只需从props获取数据并呈现它。但是,有时需要响应用户输入、服务器请求或时间流逝。为此,您需要使用state。尽量保持尽可能多的组件无状态。通过这样做,您将状态隔离到其最合适的位置并最小化冗余,使应用程序更易于理解。一个常见的模式是创建几个无状态组件,仅呈现数据,并在层次结构中将其状态传递给其子代组件的有状态组件。有状态组件封装了所有交互逻辑,而无状态组件以声明性方式处理呈现数据。- https://facebook.github.io/react/docs/interactivity-and-dynamic-uis.html#what-components-should-have-state 状态应包含组件的事件处理程序可能更改以触发UI更新的数据。在实际应用程序中,这些数据往往非常小且可JSON序列化。构建有状态组件时,请考虑其状态的最小可能表示,并仅在此.state中存储这些属性。在render()内部,根据此状态计算所需的任何其他信息。您会发现,以这种方式思考和编写应用程序往往会导致最正确的应用程序,因为将冗余或计算值添加到状态意味着您需要明确地将它们保持同步,而不是依赖于React为您计算它们。- https://facebook.github.io/react/docs/interactivity-and-dynamic-uis.html#what-should-go-in-state

2
不同意,将所有的 props 传递下去实际上比直接设置子组件的 store 效率更低。只需为子组件设置存储,就不必处理一堆 props,当 props 改变时还要更新组件。只需更新存储并让组件从其存储中获取数据即可。 - PositiveGuy
你能告诉我为什么props比states更高效吗?谢谢。 - hqt
@hqt 我认为因为它是不可变的,所以内部比较更改值的速度更快。 - Gaspar

161

通过将其与纯 JS 函数相关联,您可以最好地理解它。

简单来说,

State 是组件的本地状态,无法在组件外部访问和修改。它相当于函数中的局部变量。

纯 JS 函数

const DummyFunction = () => {
  let name = 'Manoj';
  console.log(`Hey ${name}`)
}

React 组件

class DummyComponent extends React.Component {
  state = {
    name: 'Manoj'
  }
  render() {
    return <div>Hello {this.state.name}</div>;
  }

属性(Props),则为组件提供了从其父组件接收数据的能力,使得组件可重复使用。它们相当于函数参数。

普通JS函数

const DummyFunction = (name) => {
  console.log(`Hey ${name}`)
}

// when using the function
DummyFunction('Manoj');
DummyFunction('Ajay');

React组件

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

}

// when using the component
<DummyComponent name="Manoj" />
<DummyComponent name="Ajay" />

致谢:Manoj Singh Negi

文章链接:React State vs Props explained


34
这实际上是一个非常有帮助的答案。阅读完后,我就能够理解更为详尽的答案了。谢谢。 - Aaron
8
React组件=普通JS函数是一个很好的方式来清楚地解释这一点。随着React越来越向函数组件移动,这个说法甚至更加字面意义上成立了。 - J.D. Sandifer
我认为将状态的类比与局部变量有些过于简单化了。状态经常因副作用而改变,其范围不局限于函数/组件的本地。通常情况下,状态会因HTTP调用、UX交互等副作用而发生变化。Props的值也可能因副作用而改变,但这不是组件关心的问题;这是祖先组件的问题。在(重新)渲染组件时,考虑状态的上下文可能也会有所帮助。问问自己,组件在挂载后是否需要更改其行为/外观。如果是这样,请将重新渲染条件设置为一个状态变量。 - Aaron
虽然这是一个很好的答案,但实际上状态描述了组件内部的状态,该组件可以是数组、字符串、整数或其他类型。而 prob 只是属性的简称,可以像其他语言中的参数一样赋给子组件。它仍然是一个状态,是子组件内部的属性。所以对我来说,区别只在于当前的归属。 - SamPhoenix

67

我最喜欢的 props vs state 总结在这里:react-guide 感谢那些人。以下是该页面的编辑版本:


props 和 state

精简版 如果组件在某个时刻需要更改其中一个属性,那么该属性应该成为其状态的一部分,否则它应该只是该组件的一个 prop。


props

props (短语 properties)是组件的配置。它们从上面接收并且就组件而言是不可变的。一个组件不能更改其 props,但其应负责组合子组件的 props。Props 不仅可以是数据 - 回调函数也可以作为 props 传递。

state

state 是数据结构,在组件挂载时具有默认值。随着时间推移,它可能会发生变化,主要是由于用户事件的影响。

组件内部管理自己的状态。除了设置初始状态外,组件没有处理其子组件状态的任务。你可以将状态视为该组件的私有属性。

更改 props 和 state

                                                   props   state
    父组件是否可以给它设置初始值?               可以     可以
    父组件是否可以更改它的值?                    可以     不行
    是否可以在组件内部设置默认值?*              可以     可以
    是否可以在组件内部更改它的值?                不行     可以
    是否可以为子组件设置初始值?                  可以     可以
    是否可以在子组件中更改值?                    可以     不行
  • 请注意,props 和 state 的初始值都可以从父组件中接收,如果有,则会覆盖组件内定义的默认值。

该组件应拥有状态吗?

状态是可选的。因为状态会增加复杂性并减少可预测性,所以没有状态的组件更可取。即使你显然无法在交互式应用中没有状态,你也应该避免有太多具有状态的组件。

组件类型

无状态组件 只有 props 没有 state。它们的功能除了 render() 函数外几乎没有其他东西。它们的逻辑围绕接收到的 props 进行处理,这使得它们非常容易理解和测试。

有状态组件 既有 props 也有 state。当你的组件必须保留一些状态时,就需要使用它们。这是客户端和服务器通信(XHR、WebSockets 等)、处理数据和响应用户事件的好地方。这些逻辑应该封装在适量的有状态组件中,并且所有可视化和格式化逻辑都应该向下移动到许多无状态组件中。

来源


1
你所说的“从父组件接收状态初始值”是什么意思?据我所知,状态只在单个组件的范围内定义,不能直接从外部更改。 - Maxim Kuzmin
@MaximKuzmin 我认为这是指采用类似“initialColor”这样的属性来初始化“color”状态的常见模式。该状态最初从属性(从父级接收)中获得值,然后在此之后继续像常规状态一样运作。将其包含在状态与属性介绍中可能会有点混淆,但这是一个重要的模式需要了解。 - J.D. Sandifer
我认为这是最好的答案。 - Aaron

30

Props和State的主要区别在于,State是内部状态,由组件自身控制,而Props是外部状态,由渲染组件的内容控制。

function A(props) {
  return <h1>{props.message}</h1>
}

render(<A message=”hello” />,document.getElementById(“root”));


class A extends React.Component{  
  constructor(props) {  
    super(props)  
    this.state={data:"Sample Data"}  
  }  
  render() {
    return(<h2>Class State data: {this.state.data}</h2>)  
  } 
}

render(<A />, document.getElementById("root"));

State VS Props

  • 状态可以被更改(可变的)
  • 而属性则不行(不可变的)

25

props(缩写为“属性”)和state都是普通JavaScript对象。虽然它们都保存影响渲染输出的信息,但它们有一个重要的区别:props被传递给组件(类似于函数参数),而state在组件内部管理(类似于在函数内声明变量)。

简单来说,state仅限于当前组件,而props可以传递到任何您希望的组件...您可以将当前组件的state作为prop传递给其他组件...

另外,在React中,我们有无状态组件(stateless components),它们只有props而没有内部状态...

以下示例展示了它们如何在您的应用程序中工作:

父组件(具有状态的组件):

class SuperClock extends React.Component {

  constructor(props) {
    super(props);
    this.state = {name: "Alireza", date: new Date().toLocaleTimeString()};
  }

  render() {
    return (
      <div>
        <Clock name={this.state.name} date={this.state.date} />
      </div>
    );
  }
}

子组件(无状态组件):

const Clock = ({name}, {date}) => (
    <div>
      <h1>{`Hi ${name}`}.</h1>
      <h2>{`It is ${date}`}.</h2>
    </div>
);

15

基本上,它们的区别在于状态类似于面向对象编程中的属性: 它是一个组件的局部变量,用于更好地描述它。 Props则类似于参数 - 它们从组件的调用者(父组件)传递给组件: 就像您使用特定参数调用函数一样。


8

在React中,状态属性都用于控制组件内的数据,一般情况下,属性是由父组件设置并传递给子组件,并且在整个组件中是不变的。对于将要更改的数据,我们必须使用状态。而属性是不可变的,状态是可变的,如果您想更改属性,可以从父组件中进行更改,然后将其传递给子组件。


7

根据我在使用React时学到的知识:

  • props被用于一个组件从外部环境中获取数据,即另一个组件(纯函数、函数式或类)或通用类JavaScript/TypeScript代码。

  • 状态被用于管理组件的内部环境,即组件内部数据的变化


6

Props: Props就是组件的属性,而React组件则是一个JavaScript函数。

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

const element = ;

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

状态:状态只能在组件内部访问。为了跟踪组件内的数据,我们使用状态。我们可以通过setState更改状态。如果我们需要将状态传递给子级,则必须将其作为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>);
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接