Redux est un module de gestion du state de react
Il permet de mettre le state dans un store et le rend disponible dans tout les composant de l'application ce qui permet deviter de devoir passer tout nos etat par des props et de favoriser le SOC
Avec redux classique on a acces a 3 methodes
store.getState()
: pour recuperer notre state dans notre applicationstore.dispatch({ ... })
: pour effectuer une actionstore.subscribe(() => { ... })
: pour effectuer un rendu a chaque fois qu'une valeur du state changeLe module react-redux
offre des hooks simplifier pour effecter la gestion du state
A chaque mise a jour du state, react-redux
effectue un nouveau rendu
Comme pour le module router-react-dom on englobe notre application react dans un Provider et on lui donne le store avec en paramettre
1import { BrowserRouter } from "react-router-dom"; 2import { createRoot } from "react-dom/client"; 3import { Provider } from "react-redux"; 4 5import App from "src/components/App"; 6import store from "src/store"; 7 8const rootReactElement = ( 9 <Provider store={store}> 10 <BrowserRouter> 11 <App /> 12 </BrowserRouter> 13 </Provider> 14); 15 16const root = createRoot(document.getElementById("root")); 17root.render(rootReactElement); 18
Pour crée un store avec redux plusieurs elements sont necessaire
Ici on crée notre store avec la fonction createStore et on lui donne en argument notre reducer et nos middlewares
1import { createStore, applyMiddleware, compose } from "redux"; 2 3import reducer from "src/store/reducers"; 4import dataMiddleware from "./middlewares/dataMiddleware"; 5import recipesMiddleware from "./middlewares/recipesMiddleware"; 6 7const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 8 9const enhancers = composeEnhancers( 10 applyMiddleware(dataMiddleware, recipesMiddleware) 11); 12 13const store = createStore(reducer, enhancers); 14 15export default store; 16
Ici si notre action est de type GET_DATA
on retournera un nouveau state avec la key isLoading qui aura changer
initialState
est le state par defaut au lancement de notre application1import { GET_DATA } from "../actions/recipes"; 2 3export const initialState = { 4 list: [], 5 isLoading: true, 6 favorites: [], 7}; 8 9const reducer = (state = initialState, action = {}) => { 10 switch (action.type) { 11 case GET_DATA: 12 return { 13 // on copie tout le state actuel 14 ...state, 15 // on change la valeur qui nous interesse 16 isLoading: true, // ou action.isLoading par exemple 17 }; 18 19 default: 20 return state; 21 } 22}; 23 24export default reducer; 25
On peut utiliser combineReducers
pour combiner plusieurs reducer
Les valeurs du state seront aussi separer :
state.recipe.list
state.user.email
1import { combineReducers } from "redux"; 2 3import recipesReducer from "./recipes"; 4import userReducer from "./user"; 5 6const rootReducer = combineReducers({ 7 recipes: recipesReducer, 8 user: userReducer, 9}); 10 11export default rootReducer; 12
Permet de mieux s'organier en utilisant des constantes et des fonction plutot que des string et des objet
1export const GET_DATA = "GET_DATA"; 2 3export const getData = () => ({ 4 type: GET_DATA, 5}); 6
A chaque nouveau rendu on recupere toutes les données
1import { useEffect } from "react"; 2import { getData } from "src/store/actions/recipes"; 3 4import Menu from "src/components/Menu"; 5 6function App() { 7 useEffect(() => { 8 dispatch(getData()); 9 }, []); 10 11 return ( 12 <div className="app"> 13 <Menu /> 14 </div> 15 ); 16} 17 18export default App; 19
Permet de realiser des fonction asynchrone comme par exemple des requete a une API
Il aggisent comme des videurs chaqu'un a la suite des autres.
1// ... 2import dataMiddleware from "./middlewares/dataMiddleware"; 3import recipesMiddleware from "./middlewares/recipesMiddleware"; 4// ... 5const enhancers = composeEnhancers( 6 applyMiddleware( 7 dataMiddleware, // 1er middleware a agir 8 recipesMiddleware // 2nd a agir il aura acces au state mis a jour par le 1er 9 // etcetc autant de Mw que l'on veux 10 ) 11); 12 13const store = createStore(reducer, enhancers); 14 15export default store; 16
A chaque rendu de l'application les Mw seront traverser
Si une action est capter dans un Mw une action sera effectuer et une nouvelle action sera créé a la suite
Les middleware utilise redux
et pas react-redux
on ne peut donc pas utiliser les hooks useSelector et useDispatch mais les methode du store de redux
A chaque fin d'action on next pour que le state evolue et ne reste pas figer
1import axios from "axios"; 2 3import { GET_DATA, getDataSuccess } from "../actions/recipes"; 4 5const dataMiddleware = (store) => (next) => (action) => { 6 switch (action.type) { 7 case GET_DATA: { 8 axios("http://localhost:8080/recipes") 9 .then((res) => { 10 store.dispatch(getDataSuccess(res.data)); 11 }) 12 .catch((error) => { 13 console.log(error); 14 }); 15 next(action); 16 break; 17 } 18 19 default: 20 next(action); 21 break; 22 } 23}; 24 25// equivalent 26 27// const dataMiddleware = (store) => { 28// return (next) => { 29// return (action) => { 30// // fait des truc 31// } 32// } 33// } 34 35export default dataMiddleware; 36