這一次完全搞定 useReducer - useContext使用

useReducer-基礎概念篇html

useReducer-使用篇前端

useReducer-配合useContext使用react

歡迎回到咱們的useReducer系列第三篇,若是這是你第一次看到這個系列,推薦先看看前兩篇:git

上篇文章結尾提到過使用useReducer,能夠幫助咱們集中式的處理複雜的state管理。但若是咱們的頁面很複雜,拆分紅了多層多個組件,咱們如何在子組件觸發這些state變化呢,好比在LoginButton觸發登陸失敗操做?github

這篇文章會介紹如何使用另一個高階Hook-useContext去解決這些問題。app

useContext從名字上就能夠看出,它是以Hook的方式使用React Context。先簡單介紹Context的概念和使用方式,更多Context的知識能夠參考官方文檔ide

context 介紹

下面這段定義來自官方文檔:函數

Context is designed to share data that can be considered 「global」 for a tree of React components, such as the current authenticated user, theme, or preferred language. 
複製代碼

簡單來講Context的做用就是對它所包含的組件樹提供全局共享數據的一種技術,talk is cheep 咱們直接看官方Demo:post

// 第一步:建立須要共享的context
const ThemeContext = React.createContext('light');

class App extends React.Component {
  render() {
    // 第二步:使用 Provider 提供 ThemeContext 的值,Provider所包含的子樹均可以直接訪問ThemeContext的值
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}
// Toolbar 組件並不須要透傳 ThemeContext
function Toolbar(props) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton(props) {
  // 第三步:使用共享 Context
  const theme = useContext('ThemeContext');
  render() {
    return <Button theme={theme} />;
  }
}
複製代碼

關於Context還有一個比較重要的點是:當Context Provider的value發生變化是,他的全部子級消費者都會rerender。性能

useContext版login

看完上面Demo,咱們在回過頭思考如何利用context去解決咱們問中開頭提到的子孫類組件出發reducer狀態變化。沒錯,就是將dispatch函數做爲context的value,共享給頁面的子組件。

// 定義初始化值
    const initState = {
        name: '',
        pwd: '',
        isLoading: false,
        error: '',
        isLoggedIn: false,
    }
    // 定義state[業務]處理邏輯 reducer函數
    function loginReducer(state, action) {
        switch(action.type) {
            case 'login':
                return {
                    ...state,
                    isLoading: true,
                    error: '',
                }
            case 'success':
                return {
                    ...state,
                    isLoggedIn: true,
                    isLoading: false,
                }
            case 'error':
                return {
                    ...state,
                    error: action.payload.error,
                    name: '',
                    pwd: '',
                    isLoading: false,
                }
            default: 
                return state;
        }
    }
    // 定義 context函數
    const LoginContext = React.createContext();
    function LoginPage() {
        const [state, dispatch] = useReducer(loginReducer, initState);
        const { name, pwd, isLoading, error, isLoggedIn } = state;
        const login = (event) => {
            event.preventDefault();
            dispatch({ type: 'login' });
            login({ name, pwd })
                .then(() => {
                    dispatch({ type: 'success' });
                })
                .catch((error) => {
                    dispatch({
                        type: 'error'
                        payload: { error: error.message }
                    });
                });
        }
        // 利用 context 共享dispatch
        return ( 
            <LoginContext.Provider value={dispatch}> <...> <LoginButton /> </LoginContext.Provider> ) } function LoginButton() { // 子組件中直接經過context拿到dispatch,出發reducer操做state const dispatch = useContext(LoginContext); const click = () => { if (error) { // 子組件能夠直接 dispatch action dispatch({ type: 'error' payload: { error: error.message } }); } } } 複製代碼

能夠看到在useReducer結合useContext,經過context把dispatch函數提供給組件樹中的全部組件使用 ,而不用經過props添加回調函數的方式一層層傳遞。

使用Context相比回調函數的優點:

  1. 對比回調函數的自定義命名,Context的Api更加明確,咱們能夠更清晰的知道哪些組件使用了dispatch、應用中的數據流動和變化。這也是React一直以來單向數據流的優點。

  2. 更好的性能:若是使用回調函數做爲參數傳遞的話,由於每次render函數都會變化,也會致使子組件rerender。固然咱們能夠使用useCallback解決這個問題,但相比useCallbackReact官方更推薦使用useReducer,由於React會保證dispatch始終是不變的,不會引發consumer組件的rerender。

更多信息能夠參考官方的FQA:

how-to-avoid-passing-callbacks-down

how-to-read-an-often-changing-value-from-usecallback

總結

至此useReducer系列三篇就所有結束了,咱們簡單回顧一下:

  • 若是你的頁面state很簡單,能夠直接使用useState
  • 若是你的頁面state比較複雜(state是一個對象或者state很是多散落在各處)請使用userReducer
  • 若是你的頁面組件層級比較深,而且須要子組件觸發state的變化,能夠考慮useReducer + useContext

最後慣例,歡迎你們star咱們的人人貸大前端團隊博客,全部的文章還會同步更新到知乎專欄掘金帳號,咱們每週都會分享幾篇高質量的大前端技術文章。若是你喜歡這篇文章,但願能動動小手給個贊。

參考資料

相關文章
相關標籤/搜索