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。
- 引入redux核心包。
npm install -S redux react-redux - 创建reducer,用来整合关于 state 的所有操作。容器 (store) 修改 state 时会自动调用该函数,它的返回值会成为新的 state。
- 创建store(Redux容器对象),传入 reducer 对象和 state 初始值 作为参数。 Redux的所有操作都是通过 store 对象来进行的:
- 读取state
store.getState() - 修改 state
store.dispatch({type:'ADD'})
-
设置 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>
);
- 通过 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 优点
- Redux 中的对 state 的所有操作都封装到了 reducer 函数中,可以限制 state 的修改,使 state 变得可预测,有效的避免了错误的 state 值。
- state 和 DOM 元素通过 Redux 绑定到了一起:DOM 对象的修改是在store.subscribe() 的回调函数中进行的,state每次发生变化其值就会随之变化,不需要再一次一次地手动修改。