• React整理总结(五、Redux)


    1.Redux核心概念

    纯函数
    • 确定的输入,一定会产生确定的输出;
    • 函数在执行过程中,不能产生副作用
    store

    存储数据

    action

    更改数据

    reducer

    连接store和action的纯函数
    将传入的state和action结合,生成一个新的state

    • dispatch派发action修改store
    • subscribe | unsubscribe订阅store的数据发生变化
    // store/index.js
    import { createStore } from 'redux';
    
    const initState = {
    	msg: "hello redux"
    }
    /*** 定义reducer,纯函数*****/
    function reducer(state = initState, action){
    	if (action.type === "change"){
    		return {...state, msg: action.payload.msg};
    	}
    	return state;
    }
    
    export default const store = createStore(reducer);
    
    
    // store/ actionCreator.js
    /**** 动态生成action *****/
    export const CHANGEMSGACTION = msg => ({type: 'change', payload: {msg}});
    
    // 使用的地方
    import store from "~/store";
    const unsubscribe = store.subscribe(() => {
    	console.log("::::STORE", store.getState());
    })
    unsubscribe();
    
    // 修改store中的数据
    const MSGAction = {
    	type: "change", 
    	payload: {
    		msg: "hello change",
    	}
    };
    store.dispatch(MSGAction);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • combineReducer将多个reducer合并为一个reducer,达到拆分store的目的

    2. Redux三大原则

    单一数据源
    • 整个应用程序的state被存储在一颗object tree中,并且这个object tree只存储在一个 store中:
    • R-edux并没有强制让我们不能创建多个Store,但是那样做并不利于数据的维护;
    • 单一的数据源可以让整个应用程序的state变得方便维护、追踪、修改;
    State是只读的
    • 唯一修改State的方法一定是触发action,不要试图在其他地方通过任何的方式来修改State:
    • 这样就确保了View或网络请求都不能直接修改state,它们只能通过action来描述自己想要如何修改state;
    • 这样可以保证所有的修改都被集中化处理,并且按照严格的顺序来执行,所以不需要担心race condition(竟态)的问题;
    使用纯函数来执行修改
    • 通过reducer将 旧state和 actions联系在一起,并且返回一个新的State:
    • 随着应用程序的复杂度增加,我们可以将reducer拆分成多个小的reducers,分别操作不同state tree的一部分;
    • 但是所有的reducer都应该是纯函数,不能产生任何的副作用;
      在这里插入图片描述

    3.react-redux的使用

    • 通过provider给整个app提供store
    // App.jsx
    import { Provider } from 'react-redux';
    import store from '~/store';
    
    const root = document.querySelector("#root");
    root.render(
    	<Provider store={store}>
    		<App/>
    	</Provider>
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 通过connect将组件和store连接。connect会返回一个高阶组件,接受的参数将store中的部分数据映射到组件
    // 组件中
    import React, { PureComponent } from "react";
    import { connect } from "react-redux";
    
    class MyComp extends PureComponent{
    	render(){
    		const { msg, changeMsg } = this.props;
    		return (<div>
    		<h2>{msg}</h2>
    		<input onChange={val => changeMsg(val)} />
    		</div>
    	}
    }
    /**** 将state映射到props,组件中props中就会有msg ****/
    function mapStateToProps(state){
    	return {
    		msg: state.msg
    	}
    }
    /*** 将修改store的函数添加到组件的props中 ***/
    function mspDispatchToProps(dispatch){
    	return {
    		changeMsg(msg){
    			dispatch(CHANGEMSGACTION(msg));
    		}
    	}
    }
    
    export default connect(mapStateToProps, mapDispatchToProps)(MyComp);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 异步action—中间件
      • Middleware可以帮助我们在请求和响应之间嵌入一些操作的代码,比如cookie解析、日志记录、文件压缩等操作
      • createStore的第二个参数接受一个中间件,使用react-thunk使得dispatch可以派发函数,在派发的函数中可以异步更新store。
    import { createStore, applyMiddleware } from 'redux';
    import thunk from 'redux-thunk';
    
    const reducer = (state, action) => {
    	...
    	return state;
    }
    
    const store = createStore(reducer, applyMiddleware(thunk));
    
    // actionCreator.js
    ...
    /**** 被派发的函数,需要返回一个函数,该函数接受两个参数dispatch,getState,*****/
    const fetchHomeDataAction = () => {
    
    	return (dispatch, getState) => {
    		fetch(url).then(res => {
    			dispatch(HOMEDATAACTION(res.data));
    		});
    	}
    }
    // 组件中
    function mapDispatchToProps(dispatch){
    	return {
    		fetchHomeData(){
    			dispatch(fetchHomeDataAction()); // 执行action函数
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 使用redux-thunk
      • 在创建store时传入应用了middleware的enhance函数
        通过applyMiddleware来结合多个Middleware, 返回一个enhancer;将enhancer作为第二个参数传入到createStore中;
      • 定义返回一个函数的action:
        这里不是返回一个对象了,而是一个函数;该函数在dispatch之后会被执行;

    4.Redux/toolkit

    npm install @reduxjs/toolkit react-redux

    • createSlice({name, initialState, reducers:{}})接受reducer函数的对象、切片名称和初始状态值,并自动生成切片reducer,并带有相应的actions。
      • name:用户标记slice的名词, 在之后的redux-devtool中会显示对应的名词;
      • initialState:初始化值. 第一次初始化时的值;
      • reducers:相当于之前的reducer函数.对象类型,并且可以添加很多的函数;函数类似于redux原来reducer中的一个case语句;
        • 参数一:state
        • 参数二:调用这个action时,传递的action参数;
    import { createSlice } from '@reduxjs/toolkit';
    const CounterSlice = createSlice({
    	name: "counter",
    	initialState: {
    		count: 0,
    	},
    	reducers: {
    		addNumber(state, action){
    			state.counter += action.payload;
    		}
    	}
    });
    
    export const { addNumber } = CounterSlice.action;
    export default CounterSlice.reducer;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • configureStore包装createStore以提供简化的配置选项和良好的默认值。它可以自动组合你的 slice reducer,添加你提供的任何 Redux 中间件,redux-thunk默认包含,并启用 Redux DevTools Extension。
    const store = configureStore({
    	reducer: {
    		counter: counterReducer;
    	}
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • createAsyncThunk接受一个动作类型字符串和一个返回承诺的函数,并生成一个pending/fulfilled/rejected基于该承诺分派动作类型的 thunk
    const AXIOSDataSlice = createSlice({
    	name: 'axiosdata',
    	initialState: {
    		data: []
    	},
    	reducers: {
    		setData(state, action){
    			state.data = action.payload;
    		}
    	},
    	extraReducers: {
    		/**
    		[AxiosMultidataAction.pending](state, action){
    			state.data = action.payload;
    		}
    		[AxiosMultidataAction.rejected](state, action){
    			state.data = action.payload;
    		}
    		**/
    		[AxiosMultidataAction.fulfilled](state, action){
    			state.data = action.payload;
    		}
    	}
    	/*** 链式写法 *****/
    	extraReducers: (builder) => {
    		builder.addCase(AxiosMultidataAction.pending, (state, action) => {
    			console.log("pending");	
    		}).addCase(AxiosMultidataAction.fulfilled, (state, action) => {})
    	}
    })
    export default AXIOSDataSlice.reducer;
    
    
    const AxiosMultidataAction = createAsyncThunk("axiosdata", async (extraInfo, store) => {
    // 第一个can
    	const res = await getData();
    	return res;
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • immerjs库保持数据不可变(持久化数据

    5 手写connect

    function connect(mapStateToProps, mapDispatchToProps){
    	function hoc(Component){
    		class HOCComponent extends PureComponent{
    			constuctor(props){
    				super(props);
    				this.state = mapStateToProps(store.getState());
    			}
    			componentDidMount(){
    				this.unsubscribe = store.subscribe(() => {
    					//this.forceUpdate();
    					this.setState(mapStateToProps(store.getState());
    				})
    			}
    			componentWillUnmount() {
    				this.unsubscribe();
    			}
    			
    			render(){
    				return <Component {...this.props} {...mapStateToProps(store.getState())} {...mapDispatchToProps(store.dispatch)} />
    			}
    		}
    		return HOCComponent;
    	}
    	return hoc;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
  • 相关阅读:
    一文带你深入理解【Java基础】· 注解
    宋明的结局揭示了什么
    【PYG】简单分析Planetoid()中存储Cora数据集边的数量
    Linux系统firewall开放端口
    Java学习之SPI、JDBC、SpringFactoriesLoader、Dubbo
    争议不断的AI绘画,靠啥成为了顶流?
    clip studio paint插件开发之服务套件(二)
    网络通信协议分类和IP地址
    【C++】List -- 详解
    Linux-MySQL数据库之 MHA高可用集群部署及故障切换
  • 原文地址:https://blog.csdn.net/xaishujin/article/details/134493725