iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 20
1
Modern Web

React.js 從 【0】 到【1】推坑計畫 系列 第 20

【Day 20】可能不需要redux (2) - contextAPI

昨天介紹了 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 喔!)


上一篇
【Day 19】可能不需要 redux - useReducer
下一篇
【Day 21】React 渲染機制
系列文
React.js 從 【0】 到【1】推坑計畫 30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言