昨天介紹了 useReducer 的用法,但是還缺少了 global state 的機制啊?怎麼會說可能可以不需要 redux 了?
今天就來介紹 context API ,不用 redux 就實現 global state 的功能。
這邊以之前的 counter 與 form 為例子:
首先在 react 引入 createContext :
import { createContext } from 'react';
export const CountContext = React.createContext();
const App = () => {
const [count, setCount] = useState(0);
const [formDone, setFormDone] = useState(false)
return (
<CountContext.Provider value={count}>
<Router>
<div className="App">
<p>react router example</p>
<ul>
<li><Link to="counter">Counter</Link></li>
<li><Link to="form">Form</Link></li>
</ul>
<Switch>
{/* <Route path='/counter' component={Counter}/>
<Route path='/form' component={Form}/> */}
<Route path='/counter' component={() => <Counter count={count} setCount={setCount}/>}/>
<Route path='/form' component={() => <Form formDone={formDone} setFormDone={setFormDone}/>}/>
</Switch>
</div>
</Router>
</CountContext.Provider>
);
}
export default App;
使用 createContext 建立一個 context 實體
再使用 context.Provider 包覆需要用到 context 的元件,並帶上要共享的 state。
沒錯,使用方法跟 redux 還是 87 分像呢!
現在 global state 已經傳下去了,要怎麼在其他 component 中使用呢?
hooks 又來造福我們啦! 使用 useContext。
form.js 中:
import React,{ useState, useEffect, useContext } from 'react'
import { withRouter } from "react-router";
import { useDispatch, useSelector } from 'react-redux';
import { changeTheme } from '../actions/themeActions';
import { CountContext } from '../App';
const Form = (props) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const dispatch = useDispatch();
const darkTheme = useSelector(state => state.darkTheme);
const contextValue = useContext(CountContext);
useEffect(() => {
if(darkTheme) {
document.body.style.backgroundColor = "rgba(0,0,0,0.5)";
}else{
document.body.style.backgroundColor = "white";
}
},[darkTheme]);
const handleSubmit = (e) => {
e.preventDefault();
props.setFormDone(true);
setEmail('');
setPassword('');
props.history.push('/counter');
}
return (
<div>
<form onSubmit={handleSubmit}>
<input name="email" type="text" onChange={(e) => setEmail(e.target.value)} value={email} />
<input name="password" type="password" onChange={(e) => setPassword(e.target.value)} value={password} />
<input type="submit" value="submit" />
</form>
{props.formDone ? "成功填寫表單" : "表單未完成"}
<button onClick={() => dispatch(changeTheme())}>change theme</button>
{contextValue}
</div>
)
}
export default withRouter(Form);
使用 useContext 取到剛剛傳到 Provider value 裡的東西,就可以拿來使用囉!
而當然也可以將 Reducer 管理的 state 從 provider 傳下去,如此一來我們沒有使用 redux 就達成共用 state 與建立處理 state 的 pure function 了。
例如:
const CountProvider = ({ children }) => {
const contextValue = useReducer(reducer, initialState);
return (
<CountContext.Provider value={contextValue}>
{children}
</CountContext.Provider>
);
};
雖然透過 react 內建的 context 與 useReducer hooks ,可以做到 redux 的功能,但我認為 redux 還是有存在的必要的,例如處理非同步流程時 redux 擁有 redux-thunk 或 redux-saga 等 middleware 可以使用,使用 hooks 的話可能就需要自己處理比較多東西,因此要使用什麼技術還是依照專案的需求決定囉!
(最後補充一下,redux 的底層使用的其實就是 context 喔!)