Fil d'Ariane
Mis à jour le mardi 7 mars 2017
  • 20 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

Ce cours est en vidéo.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

J'ai tout compris !

Build a ticking clock component

Connectez-vous ou inscrivez-vous pour bénéficier de toutes les fonctionnalités de ce cours !

Displaying the time

Let's add an element to our App component that displays the time. ⏰ To do this, we'll declare a variable and reference it in the component markup. 

Back in App.js, declare a variable  time  and then display it inside a new  <p>  element at the bottom of the component:

let time = new Date().toLocaleString();

class App extends Component {
  render() {
    return (
      <div className="App">
        <div className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h2>Welcome to React</h2>
        </div>
        <p className="App-intro">
          Hi {this.props.name}!
        </p>
        <p className="App-clock">
          The time is {time}.
        </p> 
      </div>
    );
  }
}

You should see two lines of text in your app now, one with your name and one with the current time when the page was refreshed.

The clock component

Since this is a JavaScript app and not just a static web page, it would be nice if the time updated itself instead of just growing stale on the page like this. In order to do that, we'll need to add some functionality.

This seems like a good time to break out the little clock into its own component!

Breaking the code out into a new component

Let's remove the whole  <p>  tag and its contents that we created to display the time, and move it to a new file called  src/Clock.js . Remember that we need the  React  object in scope to make JSX work, so let's import it at the top of the page, and define a new component class just as was done for us in the  App  component. Let's skip the destructuring in the import statement at the top so we can see more simply what's going on here:

import React from 'react';

class Clock extends React.Component {
}

export default Clock;

As is, this is a complete module. But it won't work because our new component doesn't have a  render  method. So let's add that. Now our file should look like:

import React from 'react';

let time = new Date().toLocaleString();

class Clock extends React.Component {
  render() {
    return (
      <p className="App-clock">
        The time is {time}.
      </p>
    );
  }
}
export default Clock;

Now in the  App  component we have removed the variable declaration and the  <p>  tag we created for the clock. All that remains now is to call our new component here:

class App extends Component {
  render() {
    return (
      <div className="App">
        <div className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h2>Welcome to React</h2>
        </div>
        <p className="App-intro">
          Hi {this.props.name}!
        </p>
        <Clock ></Clock>
      </div>
    );
  }
}

Now if you save all this work and visit the page again, you'll see another helpful error message. The  App  component doesn't know about the Clock component's existence yet. If you aren't sure how to remedy this, take a look at how  index.js  makes itself "aware" of the App component, and then try doing the same thing here with Clock until you get the app to work again.

Using  state 

We still haven't solved the problem of making the clock keep up with the time. One approach could be to re-render the component every second. This sounds like it might work, but React wants to manage rendering on its own, and it provides us with an interface for notifying it of changes that may require a re-render. It's called  setState , and it's the only way we should try to make direct changes to the state of the UI.

Instead of declaring a variable at the top of the file, let's make use of a feature of ES Classes called constructor functions. The constructor function is called when a class is instantiated, and it's a good place to put business that should happen during initialization. We'll use the constructor to set the initial state of the clock. Then we'll make use of the state property instead of the variable we had previously in the markup.

import React from 'react';

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      time: new Date().toLocaleString()
    };
  }
  render() {
    return (
      <p className="App-clock">
        The time is {this.state.time}.
      </p>
    );
  }
} 

export default Clock;

 super()  is a javascript keyword that is used to call functions on a parent's object.

In this case, it will call the constructor function on the base  React.Component  class. This is necessary to preserve the functionality of the constructor function of the class your component is inheriting from.  props  is being passed along as well to make sure  super  has access to the same arguments as the overwriting constructor.

You should always pass  props  when calling the super constructor in a component class.

Check your app page now. It should be exactly the same.

We still haven't made the clock dynamic, but we've set up the groundwork for it.

 state  is private

 state  is a private property of components, a piece of information that only they can read and only they can change. Nothing outside of a component should ever know about a component's state. If a component wants to share information about its state with another component, it can do that by passing it explicitly via props in the call to the other component.

Making the clock tick with lifecycle hooks

Next we are going to make the clock tick. We'll use a simple JavaScript  setInterval  to make an update every second to the time object. The question is, where to do this?

We could do it at the top of the file, but since we're using  this.state  to keep track of the time, we won't have access to that there.

We could do it in the constructor function, which would work, but that will start a timer when the Component is instantiated, whereas we want to start our timer when the component's output is inserted into the DOM for the first time. Fortunately React provides us with special lifecycle hooks for the different stages of a component's life in the DOM.

When a component's output is first inserted into the DOM it will trigger a hook called  componentDidMount , and when it's removed from the DOM it will trigger one called  componentWillUnmount . Let's use these to setup our clock when the component is mounted, and to remove our interval when the component is unmounted, so that the interval doesn't hog memory after it's no longer in use. We can insert these right into our class definition:

  componentDidMount() {
    this.intervalID = setInterval(
      () => this.tick(),
      1000
    );
  }
  componentWillUnmount() {
    clearInterval(this.intervalID);
  }

We store the interval's id so that we can clear it later when the component is unmounted. Note that we store it directly on  this . We could store it in a variable initialized at the top of the file, but we shouldn't use  state  to store this, because it's not being used in render. From the documentation:

If you don't use something in render(), it shouldn't be in the state.

The arrow function is being used here to preserve the scope of  this .

Now we'll add the method that will trigger React to update. Here's where  state  is especially useful. If we want React to know about our changes, we don't update  state  directly. Instead we'll use  setState , which merges our desired updates into  state  and also informs React of the changes. From there, React will keep track of where in the DOM our changes need to be updated.

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

Now our component class should look like this:

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      time: new Date().toLocaleString()
    };
  }
  componentDidMount() {
    this.intervalID = setInterval(
      () => this.tick(),
      1000
    );
  }
  componentWillUnmount() {
    clearInterval(this.intervalID);
  }
  tick() {
    this.setState({
      time: new Date().toLocaleString()
    });
  }
  render() {
    return (
      <p className="App-clock">
        The time is {this.state.time}.
      </p>
    );
  }
}

When you save this file and visit the page, you should see the ticking clock!

Using  props and  state  while setting state

Sometimes when using  setState  you want to refer to the  props  object or to the previous  state . To do this you must use a second form of  setState  that accepts a function as an argument, rather than directly passing the state object. This is because the value of  state  and  props  within a call to set state may be unpredictable, since React may choose to set state asynchronously. Given some arbitrary logic that looks like this:

doTheMultiplication() {
  this.setState({
    value: this.state.thingToMultiply * this.props.multiplier
  });
}

the fix would be to refactor to:

doTheMultiplication() {
  this.setState(function (prevState, props) {
    return {
      value: prevState.thingToMultiply * props.multiplier
    };
  });
}

 

Exemple de certificat de réussite
Exemple de certificat de réussite