réagir-redux (de superficiel à profond)


Illustration
Insérer la description de l'image ici

1. redux

npm installer redux --save

1.1 Présentation et principes d'utilisation

  • C'est un outil dédié 状态管理的js库(pas un plugin React)
  • Fonction : gérer de manière centralisée l'état partagé par plusieurs composants dans les applications React
  • utiliser:
    • L'état d'un certain composant doit être disponible à tout moment pour les autres composants.(共享)
    • Un composant doit changer l'état d'un autre composant(通信)

1.2 Flux de travail

Le diagramme de workflow redux est le suivant :
Insérer la description de l'image ici

1.2.1 Trois noyaux

  • action
    • objet de l'action
    • Contient deux attributs
      • type: Identificateur d'attribut, la valeur est une chaîne, attribut unique et nécessaire
      • data: Attribut de données, valeur de tout type, attribut facultatif
      • exemple{type:'CHANGE_NAME', data: {name: 'why'}}
  • réducteur
    • Utilisé pour l'initialisation et l'état de traitement
    • Pendant le traitement, un nouvel état est généré sur la base de l'ancien état et de l'action.纯函数
    • exemple if (type === 'CHANGE_NAME') {return { ...state, name }}

magasin

  • Objets qui connectent , state,actionreducer
  • Pendant le traitement, state和actionun nouvel état est généré sur la base de l'ancien.纯函数

1.3 magasin

  • L'ensemble du fichier est divisé en modules
│  └─ store
│     ├─ actions // actions,文件夹内以模块区分
│     │  ├─ count.js
│     │  └─ person.js
│     ├─ constants.js // action type唯一标识常量
│     ├─ index.js // 入口文件
│     └─ reducer // reducer,文件夹内以模块区分
│        ├─ conut.js
│        ├─ index.js // reducer统一暴露文件,合并reducers
│        └─ persons.js
  • Introduit createStore, spécifiquement utilisé pour créer l'objet de magasin le plus central dans Redux, et redux-thunkapplyMiddleware est utilisé pour prendre en charge les actions asynchrones.

npm je redux-pensez

// src/store/index.js
import {
    
     createStore, applyMiddleware } from "redux";
// 用于支持异步action
import thunk from "redux-thunk";
import reducers from "./reducers";
export default createStore(reducers, applyMiddleware(thunk));

1.4 action

  • Définir type类型des valeurs constantes dans les objets d'action
// src/store/constants.js
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
  • Créer une action, un retour d'action 对象, un retour d'action asynchrone 函数peut être utilisé pour envoyer des requêtes réseau, une exécution setTimeout, etc.
// src/store/actions/count.js
import {
    
     INCREMENT, DECREMENT } from "../constants";
// 普通action的值为object `{type: INCREMENT, data }`
export const increment = data => ({
    
     type: INCREMENT, data });
export const decrement = data => ({
    
     type: DECREMENT, data });
export const incrementAsync = (data) => {
    
    
  return (dispatch) => {
    
    
    setTimeout(() => {
    
    
      dispatch(increment(data));
    }, 500);
  };
};
  • 异步action
    • L'action retardée ne veut pas être transmise au composant lui-même, mais à l'action
    • Exécutez des méthodes asynchrones pour agir sur le statut, mais les données spécifiques doivent être renvoyées par la tâche asynchrone

1,5 réducteur

  • La fonction réductrice recevra deux paramètres : l'état précédent (state)et l'objet d'action.(action)
  • Obtenu à partir de l'objet d'action type,data
  • Déterminer typecomment traiter les données
  • reducerLorsqu'il n'y a pas de valeur d'initialisation, c'est undefinedpour pouvoir définir la valeur initialeinitialState
import {
    
    INCREMENT, DECREMENT} from '../constants'
// 初始化状态
const initialState= 0;

export default function count(state = initialState, action) {
    
    
  const {
    
     type, data } = action;
  switch (type) {
    
    
    case INCREMENT:
      return state + data;
    case DECREMENT:
      return state - data;
    default:
      return state;
  }
}

注意

  • state只读

    • La seule façon de modifier l'État doit être le déclenchement action. N'essayez pas de modifier l'État d'une autre manière : cela garantit que toutes les modifications sont traitées et 集中化处理exécutées dans un ordre strict, il n'y a donc pas lieu de s'inquiéter des conditions de concurrence.
  • Utiliser 纯函数pour effectuer des modifications

    • Connectez l'ancien état et les actions via le réducteur et renvoyez un nouvel état :
    • À mesure que la complexité de l’application augmente, nous pouvons reducer拆分成多个小的reducersopérer séparément ;
    • Mais tous les réducteurs devraient l'être 纯函数, et ne peuvent en produire aucun 副作用;

1.5.1 Fusion des réducteurs

Grâce à combineReducersla fusion, le paramètre reçu est un objet et la valeur clé de l'objet est cohérente avec la valeur clé de l'objet obtenue par getState().

// src/store/reducers/index.js
import {
    
     combineReducers } from "redux";
import count from "./conut";
import persons from "./persons";
export default combineReducers({
    
    
  count,
  persons,
});

1.6 Envoi getState et mise à jour du statut

  • Le composant récupère getState()les données du magasin
  • dispatchdéclencher une action
  • Subscribe() termine la mise à jour de la vue
import store from "../../store";
import {
    
     increment } from "../../store/action/count";
//redux内部不支持自动更新,需要通过subscribeAPI监听redux中状态变化,只有变化,就需要重新调用render
 componentDidMount() {
    
    
    store.subscribe(() => {
    
    
      this.forceUpdate();
    });
}
  clickIncrement = () => {
    
    
    store.dispatch(increment(+1));
  };
 render() {
    
    
    return (
      <div>
        <h1>当前求和为: {
    
    store.getState()}</h1>
        ...
        <button onClick={
    
    this.clickIncrement}>+</button>
      </div>  
      )
 }

2. réagir-redux

2.1 Caractéristiques de base

  • redux doit surveiller les modifications du magasin pour mettre à jour la vue et l'utiliserstore.subscribe(() => { this.forceUpdate(); }) ; React-redux n'a pas besoin de surveiller
  • réagir-redux divise les composants enUI组件 ; 容器组件les opérations redux sont toutes dans des composants conteneurs.
  • Principe de responsabilité unique ; en connect(mapStateToProps, mapDispatchToProps)(UI)connectant les composants du conteneur et les composants de l'interface utilisateur ; redux n'a aucune distinction
  • Le composant d'interface utilisateur est responsable UI的呈现et le composant conteneur est responsable 管理数据和逻辑. Si un composant possède à la fois une logique d'interface utilisateur et une logique métier, divisez-le selon la structure suivante : un composant conteneur à l'extérieur et un composant d'interface utilisateur à l'intérieur. Le premier est chargé de communiquer avec l’extérieur, de transmettre les données au second, et le second restitue la vue.
    Insérer la description de l'image ici

2.2 connect()、mapStateToProps

  • React-ReduxFournit connectdes méthodes pour UI组件générer à partir de容器组件
  • Dans le code ci-dessous, CountUIoui UI组件, connectle dernier exporté est容器组件
  • Afin de définir la logique métier, les deux aspects d’informations suivants doivent être fournis :

Logique d'entrée : comment convertir des données externes (c'est-à-dire state对象) en paramètres des composants de l'interface utilisateur
Logique de sortie : comment convertir les actions émises par les utilisateurs en objets Action et les transmettre à partir des composants de l'interface utilisateur

connectLa méthode accepte deux paramètres : mapStateToPropset mapDispatchToProps. Ils définissent la logique métier du composant de l'interface utilisateur. Le premier est responsable de la logique d'entrée, qui est statemappée aux paramètres du composant UI ( props), et le second est responsable de la logique de sortie, c'est-à-dire que l'opération de l'utilisateur sur le composant UI est mappée pour Action
mapStateToPropsrecevoir statedes paramètres, mapDispatchToPropsrecevoir dispatchdes paramètres

// 容器组件
import {
    
     connect } from "react-redux";
import CountUI from "../../components/count";
import {
    
    
  createIncrementAction,
  createDecrementAction,
  createIncrementAsyncAction,
} from "../../redux/count_action";

const mapStateToProps = (state) => ({
    
     count: state });

const mapDispatchToProps = (dispatch) => ({
    
    
  increment: (number) => {
    
    
    dispatch(createIncrementAction(number));
  },
  incrementAsync: (number) => {
    
    
    dispatch(createIncrementAsyncAction(number, 500));
  },
  decrement: (number) => {
    
    
    dispatch(createDecrementAction(number));
  },
});
export default connect(mapStateToProps, mapDispatchToProps)(CountUI);
// UI组件
import React, {
    
     Component } from "react";

export default class CountUI extends Component {
    
    
  // 加法
  increment = () => {
    
    
    const {
    
     value } = this.selectNumber;
    this.props.increment(value * 1);
  };

  // 减法
  decrement = () => {
    
    
    const {
    
     value } = this.selectNumber;
    this.props.decrement(value * 1);
  };

  // 奇数加
  incrementIfOdd = () => {
    
    
    if (this.props.count % 2 === 1) {
    
    
      const {
    
     value } = this.selectNumber;
      this.props.increment(value * 1);
    }
  };

  // 异步加
  incrementAsync = () => {
    
    
    const {
    
     value } = this.selectNumber;
    this.props.increment(value * 1);
  };

  render() {
    
    
    return (
      <div>
        <h1>当前求和为: {
    
    this.props.count}</h1>
        ...
      </div>
    );
  }
}

2.3 mapDispatchToProps

mapDispatchToPropsEst connectle deuxième paramètre de la fonction, utilisé pour établir store.dispatchle mappage des paramètres des composants de l'interface utilisateur aux méthodes. En d'autres termes, il définit quelles opérations utilisateur doivent être traitées comme Action, et Store
il peut s'agir d'une fonction ou d'un objet qui lui est transmis.

  • Si mapDispatchToProps est une fonction
/ 容器组件
const mapDispatchToProps = (dispatch) => ({
    
    
  increment: (number) => {
    
    
    dispatch(createIncrementAction(number));
  },
  incrementAsync: (number) => {
    
    
    dispatch(createIncrementAsyncAction(number));
  },
  decrement: (number) => {
    
    
    dispatch(createDecrementAction(number));
  },
});

// mapDispatchToProps的一般写法,返回function
export default connect(mapStateToProps, mapDispatchToProps)(CountUI);
  • Si mapDispatchToProps est un objet

    La valeur clé est une fonction, Action creatoret la valeur renvoyée sera automatiquement émise ActionparRedux

// mapDispatchToProps的简写,返回object
export default connect(mapStateToProps, {
    
    
  increment: createIncrementAction,
  incrementAsync: createIncrementAsyncAction,
  decrement: createDecrementAction,
})(CountUI);

2.4Fournisseur

connectUne fois que la méthode a généré le composant conteneur, celui-ci doit récupérer statel'objet pour générer les paramètres du composant UI.
Une solution consiste à transmettre statel'objet en tant que paramètre au composant conteneur. Cependant, cela est plus gênant, d'autant plus que le composant conteneur peut se trouver à un niveau très profond, et qu'il est très gênant de transmettre l'état niveau par niveau.

// src/App.js
import React, {
    
     Component } from "react";
import Count from "./container/count";
import store from "./redux/store";

export default class App extends Component {
    
    
  render() {
    
    
    return <Count store={
    
    store} />;
  }
}
  • React-Redux fournit Providerdes composants qui permettent aux composants du conteneur d'obtenir un état
  • ProviderUne couche est enroulée autour du composant racine, afin que tous les sous-composants de l'application puissent obtenir un état par défaut.
  • Son principe est que les propriétés Reactdu composantcontext
  • Faites de l'application entière d'origine Providerun sous-composant pour la recevoir et transmettez-la au composant descendant Redux的store作为propsvia l'objet.contextconnect
// index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import {
    
     Provider } from "react-redux"
import store from "./store"

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  // <React.StrictMode>
    <Provider store={
    
    store}>
      <App />
    </Provider>
  // </React.StrictMode>
);

2.5. Middleware, fonction combineReducers, redux-devtools

  • intergiciel

    • Le but du middleware est d'étendre une partie de votre propre code entre ce qui dispatchest réalisé actionet ce qui est finalement réalisé ;reducer
    • Par exemple 日志记录, 调用异步接口, 添加代码调试功能etc. ;
    • redux-thunk, applyMiddlewareutilisé pour prendre en charge les actions asynchrones,
  • combineReducersfonction

    • Il fusionne également les réducteurs que nous avons transmis dans un objet et renvoie finalement une fonction de combinaison (équivalente à notre fonction de réduction précédente) ;
    • Lors de l'exécution de la fonction de combinaison, il décidera s'il faut renvoyer l'état précédent ou le nouvel état en jugeant si les données renvoyées avant et après sont les mêmes ;
    • Le nouvel état déclenchera le rafraîchissement correspondant de l'abonné, tandis que l'ancien état peut effectivement empêcher le rafraîchissement de l'abonné ;
  • redux-devtools

    • Grâce à cet outil, nous pouvons savoir à chaque fois 状态是如何被修改的,修改前后的状态变化
import {
    
     applyMiddleware, compose, createStore,combineReducers  } from 'redux';
import thunk from 'redux-thunk';
import count from "./conut";
import persons from "./persons";
const reducer= combineReducers({
    
    
  count,
  persons,
});
//创建store 传递reducer
// redux-devtools
// trace 开启
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
    
     trace: true }) || compose;
const store = createStore(reducer, composeEnhancers(applyMiddleware(thunk)))

3. Boîte à outils Redux

3.1 Fonctionnalités de base et API

  • La boîte à outils Redux est également connue sous le nom deRTK

npm install @reduxjs/toolkit réagir-redux

  • Les API de base de Redux Toolkit sont principalement les suivantes :
    • configureStore:Wrapper createStorepour fournir des options de configuration simplifiées et de bonnes valeurs par défaut. Il compose automatiquement le vôtre slice reducer, en ajoutant tout middleware Redux que vous fournissez, redux-thunk est inclus par défaut et en activant l'extension Redux DevTools.
    • createSlice: accepte reducer函数的对象les noms de tranches et les valeurs d'état initiales, et génère automatiquement des réducteurs de tranches avec les actions correspondantes.
    • createAsyncThunk : prend une chaîne de type d'action et une fonction qui renvoie une promesse, et génère un pending/fulfilled/rejectedmessage qui distribue le type d'action en fonction de cette promesse

3.2 créerSlice

En createSlicecréant un slice, createSlice contient principalement les paramètres suivants :

  • name: Le nom marqué par l'utilisateur comme slice, le nom correspondant sera affiché plus tard dans redux-devtool ;
  • initialState: Valeur d'initialisation, la valeur à la première initialisation ;
  • reducers: Equivalent à la fonction réducteur précédente
    • Type d'objet, et de nombreuses fonctions peuvent être ajoutées ;
    • La fonction est similaire à une instruction case dans le réducteur original de redux ;
    • Paramètres de fonction : state和actionappelez ceci action时,传递的action参数 ;
  • extraReducersÉcoutez les résultats asynchrones

createSlice返回值是一个对象, y compris toutes les actions ;

import {
    
     createSlice } from '@reduxjs/toolkit';
const homeReducer = createSlice({
    
    
	name: 'home',
	initialState: {
    
    
		banners: [],
		recommends: []
	},
	reducers: {
    
    
		changeRecommendActios(state, {
     
      paylaod }) {
    
     
			state.recommends=paylaod
		},
		changeBannerActions(state, {
     
      payload }) {
    
     
			state.banners=payload
		}
	}
})

export const {
    
     changeBannerActions, changeRecommendActios } = homeReducer.actions
export default homeReducer.reducer

3.3 création de magasin

  • configureStorePour la création store对象, les paramètres communs sont les suivants :
    • reducer, les réducteurs de la tranche peuvent être composés en un objet et transmis ici ;
    • middleware: Vous pouvez utiliser des paramètres pour transmettre d'autres middlewares
    • devTools: s'il faut configurer l'outil devTools, la valeur par défaut est true ;
import {
    
     configureStore } from '@reduxjs/toolkit';
import counterReducer from './modules/Counter';
import homeReducer from './modules/Home';
const store = configureStore({
    
    
	reducer: {
    
    
		//  这里做分包
		counter: counterReducer,
		home: homeReducer
	}
})
export default store

3.4 Fournir, connecter

  • Fournir doit encore fournir un magasin
import React from "react";
import ReactDOM from "react-dom/client";
import {
    
     Provider } from 'react-redux';
import App from "./App";
import store from './store';
const root = ReactDOM.createRoot(document.getElementById("root"));
//  1. react-redux使用 第一步提供全局store
root.render(
	//  严格模式 render执行两次
	<Provider store={
    
    store} >
		<App />
 </Provider>
);

3.5 Opérations asynchrones de Redux Toolkit

Redux Toolkit a hérité par défaut des fonctions liées à Thunk :createAsyncThunk

import {
    
     createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import axios from 'axios';
export const fetachHomeActions = createAsyncThunk('home/multidata', async (payload, extraInfo) => {
    
    
	const res = await axios.get("http://123.207.32.32:8000/home/multidata")
	return res.data
})
  • Lorsque l'action créée par createAsyncThunk est distribuée, il y aura trois états :
    • pending: L'action est lancée, mais il n'y a pas encore de résultat final ;
    • fulfilled: Obtenez le résultat final (résultat avec valeur de retour);
    • rejected: Il y a une erreur ou une exception est levée pendant l'exécution ;
import {
    
     createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import axios from 'axios';
export const fetachHomeActions = createAsyncThunk('home/multidata', async (payload, extraInfo) => {
    
    
	const res = await axios.get("http://123.207.32.32:8000/home/multidata")
	return res.data
})
const homeReducer = createSlice({
    
    
	name: 'home',
	initialState: {
    
    
		banners: [],
		recommends: []
	},
	reducers: {
    
    
		changeRecommendActios(state, {
     
      paylaod }) {
    
    
			state.recommends = paylaod
		},
		changeBannerActions(
			state, {
     
      payload }
		) {
    
    
			state.banners = payload
		}
	},
	// 异步操作(三种状态)
	extraReducers: {
    
    
		[fetachHomeActions.pending](state, action) {
    
    
			console.log(action);
		},
		[fetachHomeActions.fulfilled](state, {
     
      payload }) {
    
    
			state.banners=payload.data.banner.list
		},
		[fetachHomeActions.rejected](sate, action) {
    
    
		}
	}
})

export const {
    
     changeBannerActions, changeRecommendActios } = homeReducer.actions
export default homeReducer.reducer

appel en chaîne

// 异步操作(三种状态)(链式调用的形式)
	extraReducers: (builder) => {
    
    
		builder.addCase(fetachHomeActions.pending, (state, action) => {
    
    
			console.log("fetachHomeActions pending")
		}).addCase(fetachHomeActions.fulfilled, (state, {
     
      payload }) => {
    
    
			state.banners = payload.data.banner.list
			state.recommends = payload.data.recommend.list
		})
	}

4. crochets,useSelector、useDispatch

  • createSlice crée un réducteur
import {
    
     createSlice } from '@reduxjs/toolkit';
const initialState = {
    
    
  value: 0,
};
export const counterSlice = createSlice({
    
    
  name: 'counter',
  initialState,
  reducers: {
    
    
    increment: (state) => {
    
    
      state.value += 1;
    },
    decrement: (state) => {
    
    
      state.value -= 1;
    },
  },
})
  • Utiliser useSelectorl'état d'accès/d'utilisation
const {
    
    counter} = useSelector((state) => state.counter.counter);

// 优化
// 1. memo包裹只有只有props改变才会重新渲染
  // 2.第二个参数shallowEqual 进行浅层比较 (就是reducer中返回完全相同的对象  才不进行重新渲染)
  // shallowEqual 解决使用相同的参数调用时,useSelector返回不同的结果。这可能导致不必要的重新渲染。
const App = memo((props) => {
    
    
  const {
    
     counter } = useSelector((state) => ({
    
    
    counte: state.counter.counter
  }),shallowEqual)
  return (
    <div>
      <h1>{
    
    counter}</h1>
    </div>
  )
})
  • En utilisant useDispatchle changement d'état,
    useDispatch reçoit l'action dans les réducteurs que vous avez définis dans createSlice. Par exemple, passer l'incrément à useDispatch peut ajouter 1 à l'état.
import {
    
    useSelector, useDispatch } from 'react-redux';
import {
    
    
  decrement,
  increment,
} from './counterSlice';
export function Counter() {
    
    
 const count = useSelector((state) => state.counter.value);
 const dispatch = useDispatch();
  return (
    <div>
      <div>
        <button
          aria-label="Decrement value"
          onClick={
    
    () => dispatch(decrement())}
        >
          -
        </button>
        <span>{
    
    count}</span>
        <button
          aria-label="Increment value"
          onClick={
    
    () => dispatch(increment())}
        >
          +
        </button>
      </div>
    </div>
  );
}

Article de référence : jjjona0215 maître

Je suppose que tu aimes

Origine blog.csdn.net/weixin_46104934/article/details/131500441
conseillé
Classement