前情提要:在前面的文章有了Redux,狀態管理沒煩惱,學到了如何在 Redux 透過 Action 管理更新所有的 State。接下來就讓我們學習如何在 React 中使用 Redux 來處理資料狀態。
Redux 是一個獨立的狀態管理函式庫,它可以搭配原生 Javascript 直接撰寫出一個應用程式,也可以與其他框架像是 React、Angular 一起運作。而當 Redux 與 React 結合時,會使用 React-redux 這個延伸的函式庫,有效的來解決 React Component 間 State 複雜化的問題。
Fork 一份之前所使用的React - 計時器範例程式碼,基於這個架構從頭來使用 React-redux。
React-redux 前面建置步驟基本上跟單純使用 Redux 一樣,要事先建立 Action、Reducer、Store,所以我們直接貼上前篇建立好的三份程式碼 actions.js、reducer.js、store.js,裡面包含了 Redux 應用在計數器的內容。
actions.js
export const ADD_SCORE = "ADD_SCORE";
export const REDUCE_SCORE = "REDUCE_SCORE";
// action creator
export function addScore(idx) {
return {
type: ADD_SCORE,
payload: { idx }
};
}
export function reduceScore(idx) {
return {
type: REDUCE_SCORE,
payload: { idx }
};
}
reducer.js
import { ADD_SCORE, REDUCE_SCORE } from "./actions";
const initialState = {
members: [
{ name: "May", score: 0 },
{ name: "Julia", score: 0 },
{ name: "Selina", score: 0 }
]
};
export default function appReducer(state = initialState, action) {
switch (action.type) {
case ADD_SCORE: {
let new_members = state.members;
new_members[action.payload.idx] = {
...new_members[action.payload.idx],
score: new_members[action.payload.idx].score + 1
};
return {
members: new_members
};
}
case REDUCE_SCORE: {
let new_members = state.members;
new_members[action.payload.idx] = {
...new_members[action.payload.idx],
score: new_members[action.payload.idx].score - 1
};
return {
members: new_members
};
}
default:
return state;
}
}
store.js
import { createStore } from "redux";
import rootReducer from "./reducer";
const store = createStore(rootReducer);
export default store;
接著就讓我們正式將 React 與 Redux 結合,我們首先要做的就是讓 React Component 知道 Store,才能讀取資料和調用動作,所以就透過在 Root component 使用 <Provider>
的方式來傳遞 Store。
...
import { Provider } from "react-redux";
import store from './store'
ReactDOM.render(
<StrictMode>
<Provider store={store}>
<App />
</Provider>
</StrictMode>,
rootElement
);
要從 Store 讀取狀態,可以使用 useSelector
這個方法,直接提取 Redux Store 中的狀態數據到指定的元件中。並且它會對之前的選擇器回傳值和當前的回傳值進行比較,如果不同,相關的組件就會重新渲染。
App.js
import { useSelector } from "react-redux";
export default function App() {
const members = useSelector((state) => state.members);
return (
<div className="App">
...
{members.map((member) => (
<Member key={member.name} idx={index} />
))}
</div>
);
}
Member.js
...
import { useSelector } from "react-redux";
export default function Member(props) {
const members = useSelector((state) => state.members);
return (
<div className="member">
<h2>{members[props.idx].name}</h2>
<div>{members[props.idx].score > 0 ? "PASS" : "FAIL"}</div>
<Counter idx={props.idx} />
</div>
);
}
Counter.js
import { useDispatch, useSelector } from "react-redux";
import { addScore, reduceScore } from "../actions";
export default function Counter(props) {
const score = useSelector((state) => state.members[props.idx].score);
return (
<div className="Counter">
<h3>{score}</h3>
...
{score > 0 && (
<button>minus</button>
)}
</div>
);
}
完成從 Store 讀取資料狀態後,最後一個步驟就是要可以在組件中調用動作,來修改 State 的值。我們可以透過 useDispatch()
這個方法,發動指定的 Action 到 Reducer 中更新 State。
Counter.js
...
import { useDispatch, useSelector } from "react-redux";
import { addScore, reduceScore } from "../actions";
export default function Counter(props) {
const score = ...
const dispatch = useDispatch();
return (
<div className="Counter">
...
<button onClick={() => dispatch(addScore(props.idx))}>Plus</button>
{score > 0 && (
<button onClick={() => dispatch(reduceScore(props.idx))}>minus</button>
)}
</div>
);
}
在 React 中使用 Redux 的 React-redux 基本上就是這樣運作的,以往在 React 中需要透過提升 State 層級才能在 Component 間共同使用狀態,結合 Redux 集中狀態管理的功能,就能有效的管理複雜的 State,彌補了 React 框架的缺點更提升它的優勢。
如果文章中有錯誤的地方,要麻煩各位大大不吝賜教;喜歡的話,也要記得幫我按讚訂閱喔❤️