【译】State and Lifecycle (State和生命周期)

下面是react官方文档的个人翻译,如有翻译错误,请多多指出
原文地址:https://facebook.github.io/re…

Consider the ticking clock example from one of the previous sections.
思考一下,我们之前提到过的时钟例子。
So far we have only learned one way to update the UI.
到目前为止,我们只学到一种更新UI的方式。
We call ReactDOM.render() to change the rendered output:
我们调用ReactDOM.render()的方法来改变渲染的输出:

function tick() {
  const element = (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {new Date().toLocaleTimeString()}.</h2>
    </div>
  );
  ReactDOM.render(
    element,
    document.getElementById('root')
  );
}

setInterval(tick, 1000);

打开试试

In this section, we will learn how to make the Clock component truly reusable and encapsulated.
在这一章,我们会学到怎么把Clock组件变得真正的可重用以及封装。
It will set up its own timer and update itself every second.
这会让配置我们的timer组件并且每秒自我更新。

We can start by encapsulating how the clock looks:
我们从怎么封装Clock开始:

function Clock(props) {
  return (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {props.date.toLocaleTimeString()}.</h2>
    </div>
  );
}

function tick() {
  ReactDOM.render(
    <Clock date={new Date()} />,
    document.getElementById('root')
  );
}

setInterval(tick, 1000);

打开试试

However, it misses a crucial requirement: the fact that the Clock sets up a timer and updates the UI every second should be an implementation detail of the Clock.
然而,这会错过一个至关重要的需求: Clock设置一个计时器并且每秒钟更新UI是一个时钟的实现细节。

Ideally we want to write this once and have the Clock update itself:
理想状况下,我们先写一次然后这个Clock能自我更新:

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

To implement this, we need to add “state” to the Clock component.
要实现这一点,我们需要添加“state” 到 Clock组件。

State is similar to props, but it is private and fully controlled by the component.
stateporps很相似,但是这是组件私有的并且是受组件控制的。

We mentioned before that components defined as classes have some additional features.
我们在前面提及过,用类的形式定义组件有一些额外的功能。

Local state is exactly that: a feature available only to classes.
本地state就是:只有一个特征类。

Converting a Function to a Class

将一个函数转换为一个类

You can convert a functional component like Clock to a class in five steps:
你能通过五步把一个函数组件转化为类组件

  1. Create an ES6 class with the same name that extends React.Component. 创建一个同名的类组件并且继承React.Compoent

  2. Add a single empty method to it called render().添加一个空的方法叫做render()

  3. Move the body of the function into the render() method.把函数里面的内容移到render方法里面。

  4. Replace props with this.props in the render() body.把render()里面的props替换成this.props

  5. Delete the remaining empty function declaration.删掉之前的函数声明的组件。

class Clock extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.props.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

打开试试

Clock is now defined as a class rather than a function.
Clock现在定义成一个类组件比定义成函数组件好。

This lets us use additional features such as local state and lifecycle hooks.
因为这让我们添加一些新的功能例如本地state以及生命周期。

Adding Local State to a Class

添加State

We will move the date from props to state in three steps:
我们将用三步把props移到state

1) Replace this.props.date with this.state.date in the render() method:
render()方法里的this.props.date替换成 this.state.date

class Clock extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

2) Add a class constructor that assigns the initial this.state:
添加class constructor 用来初始化this.state

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

Note how we pass props to the base constructor:
注意,我们是怎么把props传递到constructor的:

 constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

Class components should always call the base constructor with props.
类组件应该调用constructor时候带着props

3) Remove the date prop from the <Clock /> element:
<Clock />中的 date props移除掉:

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

We will later add the timer code back to the component itself.
我们将会添加回定时器代码到组件本身。
The result looks like this:
结果如下:

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

打开试试
Next, we’ll make the Clock set up its own timer and update itself every second.
下一步,我们将把组件功能自己设置定时器并且能每秒更新。

Adding Lifecycle Methods to a Class

添加周期方法到类组件

In applications with many components, it’s very important to free up resources taken by the components when they are destroyed.
在有许多组件的应用里, 当组件被销毁的时候释放掉资源是非常重要的。

We want to set up a timer whenever the Clock is rendered to the DOM for the first time.
我们想让Clock组件在第一次渲染在DOM的时候设置定时器。

This is called “mounting” in React.
我们在React中称为”mounting”。

We also want to clear that timer whenever the DOM produced by the Clock is removed.
我们同样当组件被移除的手请

This is called “unmounting” in React.
我们在React中称为”unmounting”。

We can declare special methods on the component class to run some code when a component mounts and unmounts:
我们在类组件里生命一些特别的方法当组件mountsunmounts的时候去运行一些代码:



class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {

  }

  componentWillUnmount() {

  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

These methods are called “lifecycle hooks”.
这些方法被称为“生命周期方法钩子”。

The componentDidMount() hook runs after the component output has been rendered to the DOM. This is a good place to set up a timer:
这个componentDidMount的方法会在组件渲染在dom上后被调用。这是一个设置定时器的好地方:

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

Note how we save the timer ID right on this.
注意我们是怎么保存定时器ID的。

While this.props is set up by React itself and this.state has a special meaning, you are free to add additional fields to the class manually if you need to store something that is not used for the visual output.
this.props被初始化在React,而且 this.state有一个特殊的意义,你可以手动地自由地添加额外的字段到类中,如果你需要存储一些你不被用来输出渲染

If you don’t use something in render(), it shouldn’t be in the state.
如果你不使用render()方法,就不应该用在state里。

We will tear down the timer in the componentWillUnmount() lifecycle hook:
我们将在componentWillUnmount这个生命周期的函数方法中卸载掉定时器:

componentWillUnmount() {
    clearInterval(this.timerID);
  }

Finally, we will implement the tick() method that runs every second.
最后,我们会每一秒跑tick()方法。

It will use this.setState() to schedule updates to the component local state:
我们会用this.setState()来安排组件本地的state更新:

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

打开试试

Now the clock ticks every second.
现在,时钟每一秒都在转动。

Let’s quickly recap what’s going on and the order in which the methods are called:
让我们快速回顾一下,发生了什么并且我们是怎么去调用这些方法的。

1) When <Clock /> is passed to ReactDOM.render(), React calls the constructor of the Clock component.
<Clock />作为参数传递ReactDOM.render()的时候,React调用Clock组件中的constructor函数。
Since Clock needs to display the current time, it initializes this.state with an object including the current time.
由于Clock需要显示正确的时间,因此我们初始化this.state成一个object包含这正确的时间。
We will later update this state.
我们稍后会更新这个state
2) React then calls the Clock component’s render() method. This is how React learns what should be displayed on the screen.
React 稍后会调用Clock组件的render()方法。这就是React怎样知道什么东西应该渲染到界面上。
React then updates the DOM to match the Clock’s render output.
React 稍后会更新DOM来保证Clock正确的渲染输出
3) When the Clock output is inserted in the DOM, React calls the componentDidMount() lifecycle hook.
Clock输出被插入到DOM里面,React会调用componentDidMount()的方法。
Inside it, the Clock component asks the browser to set up a timer to call tick() once a second.
之后,Clock组件会在浏览器里设置定时器并且开始tick()方法。

4) Every second the browser calls the tick() method.
每一秒浏览器调用tick方法。
Inside it, the Clock component schedules a UI update by calling setState() with an object containing the current time.
在里面,Clock组件会安排一个UI通过调用setState()并且传入一个时间的对象来更新。
Thanks to the setState() call, React knows the state has changed, and calls render() method again to learn what should be on the screen.
由于setState()调用,React知道state的改变,并且再次调用render()方法来让界面知道怎么改变。
This time, this.state.date in the render() method will be different, and so the render output will include the updated time.
这一次,this.state.datarender()方法将会不一样,所以渲染接输出讲包括更新时间。
React updates the DOM accordingly.
React相应地更新DOM。

5) If the Clock component is ever removed from the DOM, React calls the componentWillUnmount() lifecycle hook so the timer is stopped.
如果Clock组件要从DOM外移除,React会调用componentWillUnmount()函数,并且是定时器停止。

Using State Correctly

正确使用State

There are three things you should know about setState().
有三件关于的setState的事你必须知道

Do Not Modify State Directly

别直接修改State的值

For example, this will not re-render a component:
例如,这不会重新渲染一个组件:

// Wrong
this.state.comment = 'Hello';

Instead, use setState():
我们要用setState()来代替:

// Correct
this.setState({comment: 'Hello'});

The only place where you can assign this.state is the constructor.
你唯一能声明this.state的地方只有在constructor里

State Updates May Be Asynchronous

State的更新有可能是异步的

React may batch multiple setState() calls into a single update for performance.
React 为了性能,可能会把批量处理多次的setState() 调用在一个的更新里。

Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.
因为this.propsthis.state可能异步更新了,你不应该依赖他们的值来计算下一个state

For example, this code may fail to update the counter:
例如, 下面的代码可能会更新计算器失败:

// Wrong
this.setState({
  counter: this.state.counter + this.props.increment,
});

To fix it, use a second form of setState() that accepts a function rather than an object.
为了解决这个问题,让setState()接收一个函数比接收一个对象的方式更好。
That function will receive the previous state as the first argument, and the props at the time the update is applied as the second argument:
这个函数会把前一个state作为第一个参数, 并且props在那时更新并被作为第二参数:

// Correct
this.setState((prevState, props) => ({
  counter: prevState.counter + props.increment
}));

We used an arrow function above, but it also works with regular functions:
我们在上面的函数用了箭头函数,我们使用常规的函数也一样可用:

// Correct
this.setState(function(prevState, props) {
  return {
    counter: prevState.counter + props.increment
  };
});

State Updates are Merged

state的更新是合并后的

When you call setState(), React merges the object you provide into the current state.
当你调用setState(),React会合并你提供的对象到当前的state里。

For example, your state may contain several independent variables:
例如:你的state可能包含几个独立的变量:

constructor(props) {
    super(props);
    this.state = {
      posts: [],
      comments: []
    };
  }

Then you can update them independently with separate setState() calls:
然后你就能独立更新他们通过单独调用setState()

componentDidMount() {
    fetchPosts().then(response => {
      this.setState({
        posts: response.posts
      });
    });

    fetchComments().then(response => {
      this.setState({
        comments: response.comments
      });
    });
  }

The merging is shallow, so this.setState({comments}) leaves this.state.posts intact, but completely replaces this.state.comments.
这个合并是浅合并,所以this.setState({comments})会让this.state.posts完整,但是会完全替换掉this.state.comments.

The Data Flows Down

单向数据流

Neither parent nor child components can know if a certain component is stateful or stateless, and they shouldn’t care whether it is defined as a function or a class.

所有的父组件或者子组件都不知道一个组件是stateful或者stateless的,并且他们也不应该关心自己是被定义成一个函数或者是类组件。

This is why state is often called local or encapsulated. It is not accessible to any component other than the one that owns and sets it.
这就是为什么state经常被本地调用或者被封装。对于别的组件来说,组件的拥有的state是不可被访问的。

A component may choose to pass its state down as props to its child components:
一个组件可能会把自己的state作为props传递给他们的子组件中:

<h2>It is {this.state.date.toLocaleTimeString()}.</h2>

This also works for user-defined components:
这同样适用用户定义的组件中:

<FormattedDate date={this.state.date} />

The FormattedDate component would receive the date in its props and wouldn’t know whether it came from the Clock’s state, from the Clock’s props, or was typed by hand:
FormattedDate组件将会接收data作为他的props并且将不知道他是来自哪,是Clock's state,是来自Clock's state, 还是来自手动输入的。

function FormattedDate(props) {
  return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}

打开试试

This is commonly called a “top-down” or “unidirectional” data flow. Any state is always owned by some specific component, and any data or UI derived from that state can only affect components “below” them in the tree.
这就是我们平常所说的从上往下或者单向数据流。任何的state都是属于一些特定的组件,并且任何的数据或者UI视图 只能影响在他组件树下面的的组件。

If you imagine a component tree as a waterfall of props, each component’s state is like an additional water source that joins it at an arbitrary point but also flows down.
如果你把一个组件的props想象成是瀑布,每一个组件的state就像一个额外的水资源,并且这在任意点处链接还往下流。

To show that all components are truly isolated, we can create an App component that renders three <Clock>s:
为了展示所有的组件都是孤立的,我们创建一个App组件来渲染三个<Clock>组件:

function App() {
  return (
    <div>
      <Clock />
      <Clock />
      <Clock />
    </div>
  );
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

打开试试

Each Clock sets up its own timer and updates independently.
每个Clock都会独立设置以及更新自己的定时器。

In React apps, whether a component is stateful or stateless is considered an implementation detail of the component that may change over time.
在React app里,无论一个stateful or stateless的组件都被认为组件独立的细节都可能随着时间而改变。

You can use stateless components inside stateful components, and vice versa.
你能用stateless组件代替stateful组件,反之亦然。

    原文作者:Blet
    原文地址: https://segmentfault.com/a/1190000008569394
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞