昨天介紹了 useState
的強化版工具,useReducer
,今天就來是這搭配我們先前介紹的 useContext
,讓我們可以不再用 props
,一層一層傳遞state
,我們可以從子層控制父層的狀態,那就讓我們開始吧
reducer
& createContext
首先我們先做出一個 CountContext.js
,在裡面定義,reducer
& createContext
這麼做的好處是,我們只要找到這個檔案很清楚的知道我們 初始狀態
,以及action
的觸發條件是什麼,這也讓我們可以方便去管理
// CountContext,js
import { createContext } from "react";
const initialState = { count: 0, secondCount: 10 };
const reducer = (state, action) => {
switch (action.type) {
case 'increment':
return {
count: state.count + 1,
secondCount: state.secondCount + 1,
};
case 'decrement':
return {
count: state.count - 1,
secondCount: state.secondCount - 2,
};
case 'reset':
return initialState
default:
throw new Error();
}
}
const CountContext = createContext();
export { CountContext, reducer, initialState }
因為我們是要用 Provider
去傳遞我們的狀態,所以我們在 App.js
,變更結構,並且 import
剛剛的做的 CountContext
我們將在這使用 useReducer
,並傳給 CountContext.Provider
,還是記得 Provider
只能傳遞 value 這個值,我們將 state
和dispatch
包裝成一個物件,並交由 Provider
傳遞
// App.js
import { useReducer } from "react";
import { CountContext, reducer, initialState } from "./CountContext";
import { Parent } from "./Parent";
import "./index.css"
function App() {
const [state, dispatch] = useReducer(reducer, initialState)
return (
<CountContext.Provider value={{ state, dispatch }}>
<div className="border">
APP 第一層
<div>
first Count: {state.count}
</div>
<div>
second Count: {state.secondCount}
</div>
<Parent />
</div>
</CountContext.Provider>
);
}
export default App;
現在畫面呈現是這樣,我們可以看到我們的state
都有成功出現在畫面上
好了,有了父層的傳球,接下來就是讓我們在子層去接球吧
我們將 Child.js
定義成這樣
// Child.js
import "./index.css"
import { CountContext } from "./CountContext";
import { useContext } from "react";
const Child = () => {
const value = useContext(CountContext);
const { dispatch } = useContext(CountContext);
console.log(value);
return (
<div className="child" >
child 第三層
<button onClick={()=> dispatch({type: "increment"})}>++++++</button>
<button onClick={()=> dispatch({type: "reset"})}>重置</button>
<button onClick={()=> dispatch({type: "decrement"})}>------</button>
</div>
)
}
export { Child }
並且在Child.js
,使用 useContext
去接球
我們可以將我們所接收的東西印出來看看,這就是我們要的東西
接著我們在這裡,只會使用到 dispatch
,因為我們想要在這層,去控制父層的狀態,所以我們將 dispatch
解構出來
const { dispatch } = useContext(CountContext);
這樣我們就可以在這控制父層的state
useState
呢?其實也沒有說不行,但是我們在傳遞的時候你要先把多個state
先包裝成物件,像是下面這樣,之後再交給 provider
去傳遞
// App.js
const [count, setCount] = useState(0)
const [secCount, setSecondCount] = useState(10)
const value = {
first: [count, setCount],
second: [secCount, setSecondCount]
}
// Child.js
import "./index.css"
import { CountContext } from "./CountContext";
import { useContext } from "react";
const Child = () => {
const {first, second} = useContext(CountContext);
const setCount = first[1]
const setSecondCount = second[1]
const increment = () => {
setCount(perv => perv + 1)
setSecondCount(perv => perv + 1)
}
const decrement = () => {
setCount(perv => perv - 1)
setSecondCount(perv => perv - 1)
}
const reset = () => {
setCount(0)
setSecondCount(0)
}
return (
<div className="child" >
child 第三層
<button onClick={increment}>++++++</button>
<button onClick={reset}>重置</button>
<button onClick={decrement}>-----</button>
</div>
)
}
export { Child }
這樣寫雖然也是有用,但是相對比較複雜,所以如果要使用 useContext
傳遞狀態的話,我認為配合 useReducer
,會更加清楚,且好管理
今天介紹了 useContext+
useReducer` 來管理全局狀態,來實現輕量版的 Redux,