Previously, elements were updated by periodically calling the reactdom.render () method. Now set your own timer for the Clock component:

First encapsulate the appearance:

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

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

setInterval(tick, 1000);
Copy the code

Next you need to add state to make the component self-update. To use state, you need to convert the function component into a class component.

  1. Create an ES6 class of the same name that inherits from react.component.react.ponent.
  2. Add an empty render() method.
  3. Move the function body inside the Render () method.
  4. Use this. Props instead of props in the render() method.
  5. Delete the remaining empty function declarations.
class Clock extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello,world!</h1>
        <h1>It is {this.props.date.toLocaleTimeString()}.</h1>
      </div>); }}function tick() {
  ReactDOM.render(<Clock date={new Date()} / >.document.getElementById("root"));
}
Copy the code

Clock is now a class component. The Render method is called every time the component is updated, but only one class instance of the Clock component is created and used as long as it is rendered in the same DOM node.

Then add local state

  1. Replace this.props. Date with this.state.date
  2. Add a constructor that assigns an initial value to this.state
  3. Pass props to the parent class’s constructor using the constructor
  4. Remove the date attribute from the element and assign a new date to this.state in the constructor
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"));
Copy the code

Add lifecycle methods

When the Clock component is first rendered into the DOM, it is called mount in React.

When the Clock component is removed, it is called uninstall in React.

We need to add a timer for Clock at mount time. When uninstalling, clear the timer.

ComponentDidMount () runs after the component has been rendered into the DOM, so this is the best place to set a timer.

ComponentWillUnmount () is executed just before the component is about to be destroyed, so clear the timer here.

Finally, implement the tick() method, which uses this.setState() to update the 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"));
Copy the code

Note that the this.state variable name is fixed. You cannot change this.state to something else like the function component did when it changed its input parameter “props”.

Here is the order of the calls:

  1. When passed to reactdom. render, React calls the Clock constructor to initialize this.state. Since we’re assigning a time object, the initial value of this.state is the current time, but it won’t change.
  2. React calls the render method of Clock and updates the DOM to output the Clock render.
  3. When Clock is rendered into the DOM, the componentDidMount() method is called to initialize a timer. This timer calls the tick() method once per second, and each time tick() is called, state is assigned a new time using the this.setState() method.
  4. After setState() is called, React learns that state has changed and calls render() again.
  5. When Clock is removed, the componentWillUnmount() method is called to clear the timer.

Use state correctly

  1. State cannot be modified directly
this.state.comment = 'Hello'; / / error!
this.setState({comment: 'Hello'});// correct
Copy the code

Constructors are the only places where you can assign directly.

  1. Updates to state may be asynchronous for performance reasons, and React may combine multiple setState() calls into a single call.

Because this.props and this.state may update asynchronously, you cannot rely on their values to update the next state:

this.setState({
  counter: this.state.counter + this.porps.increment,
});
// State may not be updated
Copy the code

To solve this problem, make setState() accept a function instead of an object. This function takes a parameter, props, as well as the state at which the update was applied:

this.setState((state, props) = > ({
  counter: state.counter + props.increment,
}));
Copy the code
  1. When setState() is called, React merges the supplied objects into the current state.

For example, state contains several independent variables:

constructor(props){
  super(props);
  this.state = {
    posts: [].comments: []}; }// Then call setState() respectively to update
componentDidMount(){
  fetchPosts().then(response= > {
    this.setState({
      posts: response.posts
    });
  });

  fetchComments().then(response= > {
    this.setState){
      comments: response.comments
    }
  })
}
Copy the code

The merge here is shallow, so this.setState){comments: response.comments} does not affect this.state.posts, but replaces this.state.comments. Incremental updates are implemented.

Data flows downward

Neither parent nor child can know whether a component is stateful or stateless. State is local, or encapsulated. Other than owning and setting his component, no other component is accessible.

A component can pass its state as props to its child components:

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

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>
        <FormattedDate date={this.state.date} />
      </div>
    );
  }
}

ReactDOM.render(<Clock />.document.getElementById("root"));
Copy the code

This is called top-down, one-way data flow. Any state belongs only to a particular component, and data or UI derived from that state can only affect components below it in the tree.

Think of the tree of components as a waterfall, where the state of each component is like an additional water source at that point that can only flow downward. (Draw a picture if you have time)

Each component is truly independent. If three Clock components are rendered at once, they set their own timers separately:

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

ReactDOM.render(<App />.document.getElementById("root"));
Copy the code

React lifecycle