Redux 是一個(gè)獨(dú)立的 JavaScript 狀態(tài)管理庫企蹭,不依賴于任何其他庫
1. 安裝
npm i redux
yarn add redux
2. Redux核心概念
1)state:狀態(tài)
通常我們會(huì)把應(yīng)用中的數(shù)據(jù)存儲(chǔ)到一個(gè)對(duì)象樹(Object Tree) 中進(jìn)行統(tǒng)一管理白筹,我們把這個(gè)對(duì)象樹稱為:state
state 是只讀的,這里需要注意的是谅摄,為了保證數(shù)據(jù)狀態(tài)的可維護(hù)和測(cè)試徒河,不推薦直接修改 state 中的原數(shù)據(jù)
2)reducer:純函數(shù)
什么是純函數(shù)?
- 相同的輸入永遠(yuǎn)返回相同的輸出
- 不修改函數(shù)的輸入值
- 不依賴外部環(huán)境狀態(tài)送漠,只依賴其參數(shù)
- 無任何副作用
使用純函數(shù)的好處:
- 便于測(cè)試
- 有利重構(gòu)
3)store:倉庫
為了對(duì) state顽照,Reducer,action 進(jìn)行統(tǒng)一管理和維護(hù)闽寡,我們需要?jiǎng)?chuàng)建一個(gè) Store 對(duì)象
- getState: ? getState() // 獲取 redux 存儲(chǔ)的狀態(tài)
- dispatch: ? dispatch(action) // 發(fā)起一個(gè)修改
調(diào)用 dispatch代兵,store 會(huì)調(diào)用其綁定的 reducer 函數(shù),并且會(huì)將當(dāng)前的 state 和 action 傳入 reducer爷狈。然后獲取到 reducer 的返回值植影,指定為倉庫的 state - replaceReducer: ? replaceReducer(nextReducer) // 替換掉當(dāng)前的 reducer
- subscribe //監(jiān)聽狀態(tài)發(fā)生改變
4)action:動(dòng)作
action 是一個(gè)純對(duì)象(普通對(duì)象),action 必須有一個(gè) type 屬性涎永,用于描述我們對(duì) state 做出何種修改
我們對(duì) state 的修改是通過 reducer 純函數(shù)來進(jìn)行的思币,同時(shí)通過傳入的 action 來執(zhí)行具體的操作,action 是一個(gè)對(duì)象
- type 屬性 : 表示要進(jìn)行操作的動(dòng)作類型土辩,增刪改查……
- payload屬性 : 操作 state 的同時(shí)傳入的數(shù)據(jù)
但是這里需要注意的是支救,我們不直接去調(diào)用 Reducer 函數(shù),而是通過 Store 對(duì)象提供的 dispatch 方法來調(diào)用
3.redux三大原則
- 單一數(shù)據(jù)源: 整個(gè)應(yīng)用的 state 被儲(chǔ)存在一棵 object tree 中拷淘,并且這個(gè) object tree 只存在于唯一一個(gè) store 中
- state 是只讀的: 唯一改變 state 的方法就是觸發(fā) action各墨,action 是一個(gè)用于描述已發(fā)生事件的普通對(duì)象
- 使用純函數(shù)來執(zhí)行修改
4.redux API
1)createStore(reducer, [preloadedState], enhancer);
- reducer (Function): 接收兩個(gè)參數(shù),分別是當(dāng)前的 state 樹和要處理的 action启涯,返回新的 state 樹贬堵。
- [preloadedState] (any): 初始時(shí)的 state。 在同構(gòu)應(yīng)用中结洼,你可以決定是否把服務(wù)端傳來的 state 后傳給它黎做,或者從之前保存的用戶會(huì)話中恢復(fù)一個(gè)傳給它。如果你使用 combineReducers 創(chuàng)建 - reducer松忍,它必須是一個(gè)普通對(duì)象蒸殿,與傳入的 keys 保持同樣的結(jié)構(gòu)。否則,你可以自由傳入任何 reducer 可理解的內(nèi)容宏所。
- enhancer (Function): Store enhancer 是一個(gè)組合 store creator 的高階函數(shù)酥艳,返回一個(gè)新的強(qiáng)化過的 store creator。這與 middleware 相似爬骤,它也允許你通過復(fù)合函數(shù)改變 store 接口充石。
- 返回值 (Store): 保存了應(yīng)用所有 state 的對(duì)象。改變 state 的惟一方法是 dispatch action霞玄。你也可以 subscribe 監(jiān)聽 state 的變化骤铃,然后更新 UI。
import { createStore } from "redux";
const store = createStore(reducer);
2)reducer
- reducer(state,action)
function reducer(state = {
count: 1
}, action) {
//console.log(state,action);
switch (action.type) {
case "add":
return {
count: state.count + 1
}
case "minus":
return {
count: state.count - 1
}
}
return state;
}
3)Store
- getState()
- dispatch(action)
- subscribe(listener)
- replaceReducer(nextReducer)
let unSubscribe = store.subscribe(() => {
render();
});
render();
function render() {
ReactDOM.render(<div>
<p>{store.getState().count}</p>
<button
onClick={() => {
store.dispatch({
type: "add"
})
}}
>+</button>
<button
onClick={() => {
store.dispatch({
type: "minus"
})
}}
>-</button>
<button onClick={unSubscribe}>取消監(jiān)聽</button>
<button onClick={() => {
unSubscribe = store.subscribe(() => {
render();
});
}}>添加監(jiān)聽</button>
</div>,
document.querySelector("#root")
);
}
4)combineReducers(reducers)
將 reducer 函數(shù)拆分成多個(gè)單獨(dú)的函數(shù)坷剧,拆分后的每個(gè)函數(shù)負(fù)責(zé)獨(dú)立管理 state 的一部分
import { createStore,combineReducers } from "redux";
import count from "./count";
import todo from "./todo";
const store = createStore(combineReducers({count,todo}));
export default store;
5)applyMiddleware(...middlewares) 中間件
中間件:更新的過程中惰爬,去做一些其他的事情
- dispatch ---> reducer 更新state
- dispatch --> 中間件 --> reducer
異步操作中間件:redux-thunk
- 參數(shù)是對(duì)象,直接調(diào)用 reducer 修改我們的 state
- 參數(shù)是函數(shù)听隐,調(diào)用該函數(shù)补鼻,并且把 dispatch 和 getState 傳遞我們的函數(shù)哄啄,可以在函數(shù)中雅任,進(jìn)行異步操作
store/store.js
import { createStore,combineReducers, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import count from "./count";
import todo from "./todo";
import topics from "./topics"
const store = createStore(combineReducers({count,todo,topics}),applyMiddleware(thunk));
export default store;
store/topics.js
import axios from "axios";
function topicsLoading(d,g) {
const {type} = g().topics;
return axios.get(`https://cnodejs.org/api/v1/topics?tab=${type}&page=1&limit=20`)
.then(res=>{
d({
type: "topics/load",
data:res.data.data
})
})
}
export {topicsLoading}
topics/topics.js
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { topicsLoading } from "../store/topics";
import List from "./list";
import Nav from "./navs";
function Topics() {
const {loading,type,data} = useSelector(state=>state.topics);
const d = useDispatch();
useEffect(()=>{
d(topicsLoading);
},[type]);
return <div style={{
textAlign:"center"
}}>
<Nav />
{loading?<h3>數(shù)據(jù)請(qǐng)求中……</h3>:<List data={data} />}
</div>
}
export default Topics;
5. react-redux
react項(xiàng)目中的 redux 綁定庫
1)安裝
npm i react-redux
2)<Provider store>
import ReactDOM from "react-dom";
import {Provider} from "react-redux";
import App from "./App";
import store from "./store/store";
ReactDOM.render(
<Provider
store={store}
>
<App />
</Provider>,
document.querySelector("#root")
);
3)connect()
在組件中獲取 react-redux 傳遞的 store 中的數(shù)據(jù)和操作
connect(mapStateToProps:()=>{})
mapStateToProps:根據(jù) store 中的 state,映射出 要傳遞給組件的 props
mapStateToProps 的返回值會(huì)傳遞給組件咨跌,mapStateToProps 的返回值必須是一個(gè)對(duì)象
import {connect} from "react-redux";
function Count(props) {
//console.log(props);
const {count,dispatch} = props;
return <>
<p>{count}</p>
<button onClick={()=>{
dispatch({
type: "add"
})
}}>+</button>
<button onClick={()=>{
dispatch({
type: "minus"
})
}}>-</button>
</>
}
export default connect(state=>state)(Count);
4)hooks
- useDispatch 獲取 dispatch
- useStore 獲取 store
- useSelector 獲取 state
import {useDispatch, useSelector, useStore} from "react-redux";
function Count() {
const count = useSelector(state=>state.count);
const d = useDispatch();
console.log(useStore());
return <>
<p>{count}</p>
<button onClick={()=>{
d({
type: "add"
})
}}>+</button>
<button onClick={()=>{
d({
type: "minus"
})
}}>-</button>
</>
}
export default Count;