React18学习笔记 | Redux

149 阅读4分钟

1 Reducer

1.1 Reducer干啥用

动机: 比如对购物车功能有多个操作:添加/删除食物、清除购物车,而 useState() 只给我们提供了一个 setState方法(setCartData() ),所以我们不得不在继续创建出三个不同的方法,在这三个方法中分别调用 setCartData 以实现出不同的功能。这使得对一个数据对象的不同操作和其他的函数混杂在了一起,维护起来并不方便。

reducer的作用:将那些和同一个 state 相关的所有函数都整合到一起,方便在组件中进行调用。

注意: Reducer 只适用于那些比较复杂的 state,否则就是徒增烦恼。

1.2 使用Reducer

const [state, dispatch] = useReducer(reducer, initialArg, init);

参数

第一个参数 reducer() 是一个整合函数(整合器)

  • 它的参数分别是 state 和 action (dispatch 发给它的命令)
  • 它的返回值会成为新的 state 值
  • 通常定义到组件外面,避免每次渲染都重复创建

第二个参数 initialArg 就是 state 的初始值(和 useState() 参数一样)

返回值

第一个返回值 state 用来读取 state 的值,第二个返回值是一个派发函数 dispatch(派发器)。

当调用 dispatch() 时,它会将指令发送给 reducer(),reducer() 可以根据不同的指令对 state 进行不同的处理。

const [count, setCount] = useState(1);
const addHandler = () => {
	setCount(prevState => prevState + 1);
};  
const subHandler = () => {
	setCount(prevState => prevState - 1);
};

 <button onClick={subHandler}>-</button>
 <button onClick={addHandler}>+</button>
import {useReducer, useState} from 'react';

const reducer = (state, action) => {
    switch(action.type){
        case 'add':
            return state + 1;
        case 'sub':
            return state - 1;
      	default:
         		return state;//什么都不改(避免指令传错导致state丢失)   
    }
};

function App() {
    const [count, countDispath] = useReducer(reducer,1);
    return (
        <div className="App">
            {count}
            <div>
                <button onClick={()=>countDispath({type:'sub'})}>-</button>
                <button onClick={()=>countDispath({type:'add'})}>+</button>
            </div>
        </div>
    );
}

export default App;

2 Redux(状态管理)

把 state 集中一起统一管理。

“单一数据源”思想:所有的state都会存储到一课对象树中,这个对象树会存储到一个store中。组件只需获取到store即可获取到Redux中存储的所有state。

  1. 引入redux核心包。npm install -S redux react-redux
  2. 创建reducer,用来整合关于 state 的所有操作。容器 (store) 修改 state 时会自动调用该函数,它的返回值会成为新的 state。
  3. 创建store(Redux容器对象),传入 reducer 对象和 state 初始值 作为参数。 Redux的所有操作都是通过 store 对象来进行的:
  • 读取state store.getState()
  • 修改 state store.dispatch({type:'ADD'})
  1. 设置 provider

    引入 Provider 组件把根元素包起来,注入 store 对象,那么所有组件都能访问到 store 对象了。

...
import {Provider} from "react-redux";
import store from './store';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <Provider store={store}>
        <App/>
    </Provider>
);
  1. 通过 dispatch 派发 state 的操作指令
import ReactDOM from 'react-dom/client';
import {Provider, useDispatch, useSelector} from "react-redux";
import {createStore} from "redux";

//step2:创建reducer,用来整合关于state的所有操作
//设置state和action的初始值
//根据不同指令触发不同操作的模式,让state变成一种可控可预测的状态
const reducer = (state = {
    name: 'zxx',
    age: 18,
}, action) => {
    switch (action.type) {
        case 'SET_NAME':
            return {
                ...state,
                name: action.payload
            };
        case 'SET_AGE':
            return {
                ...state,
                age: action.payload
            };
        default:
            return state;
    }
}
  	//step3: 创建store, reducer对象作为参数
		const store = Redux.createStore(reducer);

  	//step5:通过dispatch派发state的操作指令,触发reducer操作
    Btn1.addEventListener('click', () => {
        store.dispatch({type: 'SET_NAME',payload:'wxx'});
    });
    Btn2.addEventListener('click', () => {
        store.dispatch({type:'SET_AGE', payload:28});
    });

【完整逻辑】点击按钮触发 line31 事件——>line32: store 容器派发指令 ——> line8: 触发 Reducer 函数执行,reducer 根据指令({type: 'SET_NAME',...}),执行 line9,返回新的 state 值

注意: Redux是JS应用的状态容器,它并不是只能在React使用,而是可以应用到任意的JS应用中(包括前端JS,和服务器中Node.js)

网页中使用

store.subscribe订阅 state 变化的信息。该方法需要一个回调函数作为参数,当store中存储的state发生变化时,回调函数会自动调用,我们可以在回调函数中定义state发生变化时所要触发的操作。

const btn01 = document.getElementById('btn01');
const btn02 = document.getElementById('btn02');
const counterSpan = document.getElementById('counter');

const countReducer = (state = {count:0}, action) => {
    switch (action.type){
        case 'ADD':
            return {count:state.count+1};
        case 'SUB':
            return {count:state.count-1};
        default:
            return state
    }
};

const store = Redux.createStore(countReducer);
// 订阅state变化,并相应对页面DOM作出操作
store.subscribe(()=>{
    counterSpan.innerText = store.getState().count;
});

btn01.addEventListener('click', ()=>{
    store.dispatch({type:'SUB'});
});

btn02.addEventListener('click', ()=>{
    store.dispatch({type:'ADD'});
});

对比:不使用 Redux

let count = 1;

btn01.addEventListener('click', ()=>{
  count--;//state可被任意修改,处于不可预测的状态
  counterSpan.innerText = count;
});

btn02.addEventListener('click', ()=>{
  count++;
  counterSpan.innerText = count;//DOM对象需要每次都手动修改
});

【总结】Redux 优点

  1. Redux 中的对 state 的所有操作都封装到了 reducer 函数中,可以限制 state 的修改,使 state 变得可预测,有效的避免了错误的 state 值。
  2. state 和 DOM 元素通过 Redux 绑定到了一起:DOM 对象的修改是在store.subscribe() 的回调函数中进行的,state每次发生变化其值就会随之变化,不需要再一次一次地手动修改。

参考

[1] Redux – 李立超 | lilichao.com

[2] React18视频教程(讲师:李立超)React视频教程_哔哩哔哩_bilibili