svg-layout-designer-react/docs/Tutorial/Pages/BasesReact.md
2022-10-17 12:19:40 +02:00

7.2 KiB

Les bases de React

Pour commencer, je vous recommande fortement de regarder cette vidéo qui expliquera mieux que ce document sur comment fonctionne React : https://www.youtube.com/watch?v=Tn6-PIqc4UM

Ce document fera quand même de son mieux pour expliquer les bases de React et permettra d'expliquer comment intégrer un composant dans le projet.

Qu'est-ce qu'un composant React ?

Un composant React peut être décrite comme une classe (class component) ou une fonction (functional component) retournant une description de composant en JSX, un langage permettant de combiner du HTML et du JavaScript. Ces composants ont par convention l'extension de fichier .jsx (ou .tsx avec TypeScript)

Un composant est principalement constituer de props (propriétés) et d'un state (état). Cela est universel pour tous les composants.

Voici un basique exemple de composant :

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

La règle d'or est que le rendu change lorsqu'un props ou un state change.

Selon comment vous écrivez le composant, classe ou fonctionnelle, la manière de changer les props ou state est différente:

Pour les props :

// functional
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

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

Pour l'état:

// Functionnal
const useTicking = (setDate) => {
  React.useEffect(() => {
    const timerID = setInterval(() => {
      setDate(new Date());
    }, 1000);

    return () => {
      clearInterval(timerID)
    }
  }, []);
};

export const Clock = (props) => {
  const [date, setDate] = React.useState(new Date());

  useTicking(setDate);

  return (
    <div>
      { date.toLocaleTimeString() }
    </div>
  );
};


// class
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>
        {this.state.date.toLocaleTimeString()}
      </div>
    );
  }
}

Dans ce projet, il est de convention d'écrire en fonctionnel, car cela a pour avantage de fractionner les composants possèdant beaucoup de fonctions (voir par exemple ContainerOperations.ts) et de séparer les les propriétés d'états par des state hooks (crochet d'états).

La documentation qui suivra utilisera donc l'écriture fonctionnelle. Pour en savoir plus sur l'écriture de classe, vous pouvez essayer le tutoriel de React.

Props

Les props sont des données entrer d'un composant. Comme pour un composant html ils décrivent simplement ce que le composant doit représenter.

Exemple :

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

State vs Stateless

Un composant possédant un état interne utilise React.setState(newState) dans un composant de classe ou React.useState<Type>(defaultValue) dans un composant fonctionnel. Un composant ne possédant pas d'état est dit stateless.

Voici donc un exemple de composant React avec état :

// Clock.tsx
interface IClockProps {
}

const useTicking = (setTime: React.Dispatch<React.SetStateAction<Date>>) => {
  React.useEffect(() => {
    const timerID = setInterval(() => {
      setTime(new Date());
    }, 1000);
    return () => {
      clearInterval(timerID)
    }
  }, []);
};

export const Clock: React.FC<IClockProps> = (props) => {
  const [time, setTime] = React.useState<Date>(new Date());

  useTicking(setTime);

  return (
    <div>
      { time.toLocaleTimeString() }
    </div>
  );
};

Et voici la version sans état :

// Clock.tsx
interface IClockProps {
  time: Date
}

export const Clock: React.FC<IClockProps> = ({ time }) => {
  return (
    <div>
      { time.toLocaleTimeString() }
    </div>
  );
};

// Parent.tsx
const useTicking = (setTime: React.Dispatch<React.SetStateAction<Date>>) => {
  React.useEffect(() => {
    const timerID = setInterval(() => {
      setTime(new Date());
    }, 1000);
    return () => {
      clearInterval(timerID);
    };
  }, []);
};

export const Parent: React.FC<IClockProps> = ({ time }) => {
  const [time, setTime] = React.useState<Date>(new Date());

  useTicking(setTime);

  return (
    <Clock time={time}>
  );
};

Comme vous pouvez le voir, la différence est que la gestion d'état est donnée au composant parent. Cela permet de réduire les mises à jours de rendu en parallèles et de faire le rendu seulement si l'état du composant parent change.

Vous avez probablement remarqué l'usage de React.useState() et React.useEffect(). Lorsque l'on utilise une fonction avec le mot clé use de React, on utilise ce qu'on appelle un hook (crochet).

Il existe plusieurs type de hook et chacun ont leurs propre effet. Dans ce projet, nous utilisont principalement useState et useEffect mais il y a aucune interdiction pour utiliser d'autres types.

useState

useState permet de lier à l'état interne du composant une variable.

La fonction nous fourni 2 paramètres dans une liste. La valeur courante et le setter de cette valeur :

const [value, setValue] = React.useState(defaultValue);

useEffect

useEffect permet de lancer une fonction d'effet lors d'un rendu. Il prend en paramètres :

  • une fonction callback d'effet lors du rendu qui retourne lui aussi retourne une fonction d'effet lors de la suppression du rendu.
  • Il prend aussi optionnellement un liste de dépendance permettant de déterminer les conditions de rendu

Revenons sur l'exemple de useTicking:

const useTicking = (setTime) => {
  React.useEffect(() => {
    // componentDidMount
    const timerID = setInterval(() => {
      setTime(new Date());
    }, 1000);

    return () => {
      // componentWillUnmount
      clearInterval(timerID);
    };
  }, []);
};

Décrivons ce quelle fait :

  • La callback de useEffect appelle setInterval afin d'exécuter une fonction setTime toute les seconde.
  • Cette callback retourne la fonction de suppression de setInterval qui sera exécutée si le composant est disposé.
  • useEffect possède un tableu de dépendance mais elle est vide. Cela veut dire que la callback sera exécuté qu'une seule est unique fois sur toute la durée de vie du composant

Si on avait enlevé le tableau de dépendance, on aurait eu une boucle infinie ! Car rien n'aurait limité l'exécution de la callback à chaque rendu créé par setTime.

Une règle d'or est qu'il faut mettre un tableau de dépendance lorsque l'on utilise un setteur d'état car il y aura a coup sûr une boucle infinie s'il y en a pas

Contraposée: Il n'est pas nécessaire de mettre un tableau de dépendances s'il n'y pas de setteur d'état dans la callback

Il est de convention de mettre les fonctions useEffect dans une fonction nommée afin de déterminer ce qu'elle fait. Il est possible d'appeler plusieurs fois useEffect, donc il n'est pas dangereux de dissocier les effets.

Si vous avez compris, essayons de créer notre premier composant React.