Fonctionnalités de base

Qu’est ce que c’est

React.js est une librairie open-source développée par Facebook ayant pour but est de permettre le développement d’applications front-end plus rapide et plus facile à maintenir.

Exemple CodePen React


B.A.-BA

Inclure la librairie

Inclure React.js dans la page:

<!-- Note: when deploying, replace "development.js" with "production.min.js". -->
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>

Déclarer un composant

Il y a deux manières de déclarer un composant

  1. Stateless: avec une fonction JavaScript.
    Définir un composant de cette manière crée un composant sans état: il peut recevoir des données et les restituer mais ne peut pas gérer les changements de données. Le nom de la fonction doit être en UpperCamelCase et retourner soit du JSX soit null.

     const HelloWorld = function() {
         return (
             <div className='customClass'>Hello World</div>
         );
     };
     ReactDOM.render(<HelloWorld />, document.body);
    
  2. Statefull: avec une classe qui hérite de React.Component.
    Définir un composant de cette manière donne accès à toutes les fonctionnalités de React (comme les lifecycle hooks, la propriété state). Le nom de la classe doit être en UpperCamelCase et la méthode render() doit retourner soit du JSX soit null.

     class HelloWorld extends React.Component {
         render() {
             return <div className='customClass'>
                 Hello World
             </div>;
         }
     });
     ReactDOM.render(<HelloWorld />, document.body);
    

    Version ES5, on peut utiliser React.createClass:

     var HelloWorld = React.createClass({
         render: function() {
             return <div className='customClass'>
                 Hello World
             </div>;
         }
     });
     ReactDOM.render(<HelloWorld />, document.body);
    

Imbriquer des composants

Lorsque React trouve une balise HTML faisant référence à un autre composant (ex <MonComposant>), alors ce composant est crée et la balise est remplacée par le contenu retourné par la méthode render de ce composant.

Exemple:

const ChildComponent = () => {
  return (
    <div className="child">
      <p>I am the child</p>
    </div>
  );
};

class ParentComponent extends React.Component {
  render() {
    return (
      <div className="parent">
        <h1>I am the parent</h1>
        <ChildComponent />
      </div>
    );
  }
};

ReactDOM.render(<ParentComponent />, document.body);

HTML généré:

<div class="parent">
  <h1>I am the parent</h1>
  <div class="child">
    <p>I am the child</p>
  </div>
</div>

Afficher un composant dans la page

Pour ajouter un composant React à la page, il fautt appeler ReactDOM.render.

ReactDOM.render(<MyComponent />, document.body);

Caveat

Afficher une liste d’éléments

Lorsqu’on crée un liste d’élément du même type (que ce soit une balise HTML normale ou un composant), alors il est nécessaire de définir l’attribut key avec une valeur unique pour chacun. React utilise ces clés pour savoir lorsque que des éléments sont ajoutés, modifiés ou supprimés et mettre à jour le DOM en conséquence.

var list = [
  {id: 1, value: "Item A"},
  {id: 2, value: "Item B"},
  {id: 3, value: "Item C"}
];

return <ul>
  {list.map((item) =>
    <li key={item.id}>{item.value}</li>
  }
</ul>;

Video: Use the key prop

Définir le CSS d’un élément

Pour définir le style en ligne d’un élément, il faut définir un objet JS. Encore une fois, cela permet à React de minimiser les accés aux DOM.

Ajouter un composant sous condition

Si un composant ne doit s’afficher que sous certaines conditions, on peut

  1. soit ajouter un test côté parent

    render() {
      return <div>
        {this.props.b ? <MonComposant /> : null}
      </div>;
    }
    
  2. soit retourner null côté enfant

    render() {
      if(this.props.b) {
        return null;
      }
      return <div>Hello</div>;
    }
    

Afficher du HTML stocké


Propriétés

Définir des propriétés

On peut passer des propriétés aux composants

  1. Côté parent: Définir les valeurs des propriétés comme si c’était des attributs HTML

     var maVariable = "Item C";
    
     ReactDOM.render(<div>
    
       {/* Chaîne de caractère */}
       <MyComponent key="1" mytext="Item A" />
    
       {/* Valeur numérique */}
       <MyComponent key="2" mytext={10} />
    
       {/* Date */}
       <MyComponent key="3" mytext={Date()} />
    
       {/* Valeur dynamique */}
       <MyComponent key="4" mytext={maVariable + '!'} />
    
     </div>, document.body);
    
  2. Côté composant: Les valeurs des propriétés sont accessibles via this.props

     class MyComponent extends React.Component {
       render() {
         return <p>{this.props.mytext}</p>;
       }
     }
    

    Pour un composant stateless, c’est à dire si vous utilisez une fonction pour définir un composant et non pas une classe: les propriétés sont passées en paramètres.

       const MyComponent = (props) => <p>{this.props.mytext}</p>;
    

Définir du contenu JSX

On peut également passer du contenu JSX aux composants.

  1. Côté parent: Définir du contenu JSX à l’intérieur du composant

     ReactDOM.render(<div>
       	<MyComponent key="1">Item A</MyComponent>
       	<MyComponent key="2">Item B</MyComponent>
     </div>, document.body);
    
  2. Côté composant: Le contenu passé par le parent est disponible via la propriété this.props.children

     class MyComponent extends React.Component {
       render() {
         return <p>{this.props.children}</p>;
       }
     }
    

Valeur par défaut

Validation des propriétés


État

Définir l’état d’un composant

L’état d’un composant est stocké dans this.state. Toutes les données pouvant changer au cours du temps doivent être stockées dans cette variable.

class Checkbox extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      checked: false
    };
  }
  // ...
}

Version ES5:

var Checkbox = React.createClass({
  getInitialState: function() {
    return {
      checked: false
    };
  }
  // ...
});

ES6 Syntaxic sugar: On peut déclarer state en dehors du constructeur:

class Checkbox extends React.Component {
  state = {checked: false}

  render() {
    var {checked} = this.state
  }
}

Changer l’état d’un composant

Conserver this

Pour conserver this dans un callback — pour que handleCheck() soit capable d’appeller this.setState() dans notre exemple — on peut soit

  1. Lier this à la méthode en utilisant maMethode.bind(this).
    On peut le faire dans la méthode render à chaque fois qu’on veut définir un callback mais une pratique courante est de le faire directement dans le constructeur. Comme ça, on le fait qu’une seule fois.

     constructor(props) {
       // ...
       this.handleCheck = this.handleCheck.bind(this);
     }
     render() {
       // ...
           <input type="checkbox"
                  onChange={this.handleChange}
                  defaultChecked={this.state.checked} />
     }
    
  2. Utiliser une fonction flèche. Une fonction flèche lie automiquement this à partir du contexte dans lequel est elle définie.

     setMessage = () => {
       this.setState({
         message: 'Goodbye!'
       });
     }
    

Événements

value vs defaultValue

Si vous définissez l’attribut value d’un champ, alors l’utilisateur ne pourra pas éditer le contenu du champ — parce que React observe ce champ et s’assure que sa valeur dans le DOM est bien égale à celle dans le DOM virtuel. Pour définir la valeur d’un champ et permettre à l’utilisateur d’éditer le champ, deux possibilités:

Video: Controlling Form Values


Lifecycle

Il est possible de définir des méthodes sur les composants, qui seront appelées à divers moments du cycle de vie.
Les méthodes sont parfois dépréciées, supprimées ou renommées: en cas de doute, vérifier la documentation.

componentDidMount

Est appelé juste après le premier appel à la méthode render(), après que le DOM ait été construit.

Effectuer des appels API

On peut notamment s’en servir pour y effectuer des fetch — récupérer les données qu’on va par la suite afficher.

class MyComponent extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      loaded: false,
      activeUsers: null
    };
  }
  componentDidMount() {
    setTimeout( () => { // simulating an API callback
      this.setState({
        activeUsers: 1273,
        loaded: true
      });
    }, 2500);
  }
  render() {
    if(!this.state.loaded) {
      return <div>Loading data...</div>;
    }
    return <div>Active Users: {this.state.activeUsers}</div>;
  }
};

Ajouter des événements sur window/document

On peut aussi s’en servir pour ajouter des événements JavaScript.

React permet nativement d’ajouter des événements JS sur des éléments, en les passant en attribut. Cependant, si vous voulez ajouter un événément aux objets document ou window, alors vous devez le faire directement.

Pareil si vous voulez utiliser un setTimeout ou setInterval.

constructor(props) {
  this.state = {
    shouldSave: false
  };
  this.handleLeavePage = this.handleLeavePage.bind(this);
}

componentDidMount() {
  window.addEventListener('beforeunload', this.handleLeavePage);
}

componentWillUnmount() {
  window.removeEventListener('beforeunload', this.handleLeavePage);
}

// Prompt the user that there are unsaved changes
// When he leaves the page
handleLeavePage(e) {
  var e = e || window.event;

  if(!this.state.shouldSave) {
    return null;
  }
  const confirmationMessage = 'Some message';
  e.returnValue = confirmationMessage;     // Gecko, Trident, Chrome 34+
  return confirmationMessage;              // Gecko, WebKit, Chrome <34
}

Appeler à une librairie sur un élément

On peut encore s’en servir pour appeler une librairie, par exemple pour ajouter CodeMirror.

Définir l’attribut ref sur un élément dans le JSX permet de récupérer le DOMElement de cet élément. La valeur de ref doit être une fonction, et cette fonction sera appelée dès que le DOM de l’élément est construit (juste avant que componentDidMount ne soit appelé). Le DOMElement est passé en entrée à la fonction.

class MyComponent extends React.Component {
  componentDidMount() {
    console.log(this.rootNode);
  }
  render() {
    return <div ref={node => this.rootNode = node}>
        Hello World
      </div>;
  }
}

shouldComponentUpdate

Est appelé quand l’état ou les propriétés du composant changent. La méthode doit retourner une valeur booléenne pour indiquer à React si la méthode render doit être appelée ou non. C’est un moyen utile pour optimiser les performances.

shouldComponentUpdate(nextProps, nextState) {
  if(!this.codeMirror) {
    return true;
  }

  // La propriété "value" a changé:
  // mettre à jour le contenu de CodeMirror
  if(nextProps.value !== undefined
      && nextProps.value !== this.value
      && this._normalizeLineEndings(this.codeMirror.getValue())
          !== this._normalizeLineEndings(nextProps.value)) {

        this.value = nextProps.value;
        this.codeMirror.setValue(nextProps.value);
  }

  // La propriété "options" a changé:
  // mettre à jour les options de CodeMirror
  if(typeof nextProps.options === 'object') {
    for(let optionName in nextProps.options) {
      if(!nextProps.options.hasOwnProperty(optionName)) {
        continue;
      }
      var oldValue = this.codeMirror.getOption(optionName),
          newValue = nextProps.options[optionName];

      // L'option a changé, mettre à jour CodeMirror
      if(oldValue !== newValue && JSON.stringify(newValue) !== JSON.stringify(oldValue)) {
        this.codeMirror.setOption(optionName, newValue);
      }
    }
  }

  // Ne pas appeler render()
  return false;
}

Exemple CodeMirror React

componentDidUpdate

Est appelée après que le DOM du composant ait été mis à jour. Cette méthode n’est pas appelée au premier render(). On peut notamment l’utiliser pour appeler une librairie sur le HTML généré.

/**
 * Preview has been updated : highlight code samples
 */
componentDidUpdate() {
  Highlight.highlightAll();
}

componentWillUnmount

Est appelé juste avant que le composant ne soit retiré du DOM — quand on supprime l’élément ou quand on ferme la fenêtre.

Peut notamment être utilisé pour retirer des événements ajoutés dans componentDidMount ou stopper des timers / intervalles démarrés.

Video: Stop Memory Leaks with componentWillUnmount