Imprimer
Catégorie : R4A.10 - Compléments web (vuejs)
Affichages : 281

1°/ Principes de React

1.1°/ A l'origine

 

import React from "react";
class MyCounter extends React.Component {
    constructor(props) {
        super(props);
        this.state = {            
            counter: 0
        };
        this.handleClick= this.handleClick.bind(this);
    }
    handleClick() {
        this.setState({counter: this.counter+1})
    }
    render() {
        return (
            <div>
                <p>{this.props.title}</p>
                <button onClick={this.handleClick}>{this.counter}</button>
            </div>
        )
    }
}

1.2°/ Avec les hooks.

import { useState } from 'react'
function MyCounter(props) {
    const [counter, setCounter] = useState(0)
    function handleClick() {
        setCounter(counter+1)
    }
    return (
        <div>
            <p>{props.title}</p>
            <button onClick={handleClick}>{counter}</button>
        </div>
    )
}
export default MyCounter

 

2°/ Créer un projet React avec Vite

2.1°/ Création initiale

npm create vite@latest

 

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  esbuild: {
    loader: 'jsx',
  },
  optimizeDeps: {
    esbuildOptions: {
      loader: {
        '.js': 'jsx',
      },
    },
  },
})

 

 

2.2°/ Installer les plugins

Remarque : le plugin react-router-dom permet de fiare le routage dans des applis web. Pour faire de même dans des applis mobiles avec ract-native, il faut installer rect-router-native.

npm install react-router-dom
npm install react-redux
npm install @reduxjs/toolkit
npm ls
myproj@0.0.0 /home/login/myproj
├── @reduxjs/toolkit@1.9.3
├── @types/react-dom@18.0.11
├── @types/react@18.0.28
├── @vitejs/plugin-react@3.1.0
├── react-dom@18.2.0
├── react-redux@8.0.5
├── react-router-dom@6.9.0
├── react@18.2.0
└── vite@4.2.0

2.3°/ mettre en place react-router-dom

 

import {createBrowserRouter} from "react-router-dom";
// import des composants.
...
// définition des routes, par ex:
const router = createBrowserRouter([
  {
    path:'/',
    element: <App />,
    children: [
      { path: 'welcome', element: <Welcome /> }
    ]
  }
])

export default router

 

import React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
import {RouterProvider} from 'react-router-dom';
import router from "./router";

ReactDOM.createRoot(document.getElementById('root')).render(
    <React.StrictMode>
            <RouterProvider router={router}/>
    </React.StrictMode>,
)

 

 

NB : ces différents points sont abordés dans la démonstration.

 

2.4°/ Mettre en place Redux

ATTENTION ! Redux est une "usine à gaz" comparé à pinia, surtout si on veut l'utiliser avec des composants sans hooks, ou lorsque l'on veut un store qui va chercher des données auprès d'une API. Il reste cependant une "référence" dans le monde react, donc à connaître.

Exemple basique avec un compteur, dans un fichier counter.slice.js :

import { createSlice } from '@reduxjs/toolkit'

export const counterSlice = createSlice({
    name: 'counter',
    initialState: {
        value: 0
    },
    reducers: {
        increment: state => { state.value += 1 },
        decrement: state => { state.value -= 1 },
    }
})
// Action creators are generated for each case reducer function
export const { increment, decrement} = counterSlice.actions
export default counterSlice.reducer

 

 Exemple avec le slice counter :

import { configureStore } from '@reduxjs/toolkit'
import counterReducer from './counter.slice'
export default configureStore({
  reducer: {
    counter: counterReducer,
  },
})

 

Par exemple :

import { useSelector, useDispatch } from 'react-redux'
import { increment} from '../store/counter.slice'
function TestCounter() {
    const count = useSelector(state => state.counter.value)
    const dispatch = useDispatch()
    return (
        <div>
                <p>{count}</p>
                <button onClick={() => dispatch(increment)}>Click</button>
        </div>
    )
}
export default TestCount

 

Remarques :

 

3°/ Démonstration

  • Le code de démonstration est téléchargeable [ ici ]
  • Il contient uniquement le répertoire src.
  • Explorer le code de App : on remarque la structuration du code et l'utilisation de useState() et useEffect(), qui donne un code assez similaire à du vue 3.
  • Explorer le code de router/index.js : c'est relativement semblable à ce que l'on peut écrire en vue-router, même sur la syntaxe des paramètres des routes.
  • Explorer le code des composants Users et UsersWithoutHooks pour illustrer la simplicité de développement avec vs sans hooks.
  • Dans Users, on remarque l'utilisation de useParams() pour avoir accès au paramètre de la route (en l'occurrence le mot hello).
  • Dans UsersWithoutHooks, on remarque que l'on ne peut pas accéder directement aux paramètre, puisque les hooks sont interdits dans les classes. On utilise donc une astuce "pourrie" qui consiste à encapsuler le composant dans un autre, ce dernier étant créé avec la nouvelle syntaxe et permettant donc d'utiliser useParams(). Cette astuce n'est évidemment utile que lorsque l'on veut réutiliser des "anciens" composants.
  • Dans ce même composant, on remarque le problème liés aux callbacks utilisés lors de certains événements, selon qu'ils sont appelés directement ou via une fonction fléchée.
  • Par exemple handleAdd() est appelée en cas de click, mais du coup, hors du contexte de l'objet représentant le composant. Il n'y a donc pas la bonne valeur de this. C'est pourquoi dans le constructeur, on relie "de force" handleAdd() à this du composant, grâce à bind().
  • En revanche, handleRemove() est appelée grâce à une fonction fléchée. Or, une fonction fléchée est toujours reliée au contexte où elle est définie, donc son this correspond au composant. Par transitivité, le contexte d'appel de handleRemove() est le même que celui de la fonction fléchée, donc this a bien la bonne valeur.
  • Pour illustrer ce qui arrive en cas de this invalide, clique sur le bouton du bas. Il appelle la fonction log() qui elle-même essaye d'accéder à this.state. Comme this n'a pas la bonne valeur, il n'y a pas d'attribut state, donc la console affiche une erreur. Pour éviter cette erreur, il faudrait soit appeler log() via une fonction fléchée lors du clic, soit la relier de force au this du composant.
  • Explorer le contenu des différents slices. Celui de towns.slice.js permet d'illustrer le concept de thunk et de la gestion "usine à gaz" du cycle de vie du thunk, pour tenir compte du caractère asynchrone du traitement.
  • En revanche, les autres slices sont relativement simples et pas très éloignés de vuex.
  • Explorer le contenu du composant Towns. On remarque que le code est très simple pour récupérer la liste de villes : toute la complexité est dans le slice.