天天看點

React Hook借助useReducer, useContext代替Redux方案

React Hook借助useReducer, useContext代替Redux方案

    • 目錄結構圖
    • 效果圖
    • 實作
      • `Test/reducer.jsx`
      • `Test/child.jsx`
      • `Test/index.jsx`
      • `OtherPage/index.jsx`
    • 注意點
當我們使用

redux

進行資料管理的時候,一般都是在根元件通過

Provider

的方式引入

store

,然後在每個子元件中,通過

connect

的方式使用高階元件進行連接配接,這樣造成的一個問題是,大量的高階元件代碼備援度特别高,既然

hooks

帶來了新特性,不如一起來用用看

目錄結構圖

React Hook借助useReducer, useContext代替Redux方案
├── otherPage // 其他頁面
|   ├── index.jsx // 共享`Test`頁面的狀态;
├── Test // 測試頁面
|   ├── child.jsx // 測試頁面的子元件。1、`useContext`定義的位置,擷取父元件提供的`context`;2、`useEffect`進行異步請求;
|   ├── index.jsx // 測試頁面父元件。1、通過使用`Provider`提供給子元件`context`;2、`useReducer`定義的位置,引入一個`reducer`并且提供初始狀态`initialState`;
|   ├── otherPage.jsx // 其他頁面,已删除~~
|   └── reducer.jsx // 處理不同類型的`action`操作
           

效果圖

React Hook借助useReducer, useContext代替Redux方案

實作

Test/reducer.jsx

import axios from 'axios';

function reducer (state, action) {
  switch (action.type) {
    case 'ADD': // 加
      return Object.assign({}, state, {
        type: 'add',
        index: ++state.index
      });
    case 'DOWN': // 減
      return Object.assign({}, state, {
        type: 'down',
        index: --state.index
      });
    case 'FETCH': //請求
      axios('/addFetch').then((result) => {
        console.log(result);
      }).catch((err) => {
        console.log(err);
      }); ;
      return Object.assign({}, state);
    default: // 重置
      return Object.assign({}, state, {
        index: 1
      });
  }
};

export default reducer;

           

Test/child.jsx

import React, { useContext, useEffect } from 'react';
import { FetContext } from './index';
import { Button } from 'antd-mobile';

function DeepChild (props) {
  // If we want to perform an action, we can get dispatch from context.
  const dispatch = useContext(FetContext);

  function handleClick () {
    dispatch({ type: 'ADD' });
  }

  const fetch = () => {
    console.log('fetch');
    dispatch({ type: 'FETCH' });
  };

  useEffect(() => {
    console.log('child useEffect', props);
  });

  return (
    <div>
      <Button onClick={handleClick} type='primary'>Child Add</Button>
      <br />
      <Button onClick={fetch} type='primary'>Child Request</Button>
      <br />
    </div>
  );
}

export default DeepChild;
           

Test/index.jsx

/* eslint-disable react/prop-types */
import React, { useReducer, useEffect } from 'react';
import reducer from './reducer';
import DeepChild from './child';
import { Button, InputItem } from 'antd-mobile';
export const FetContext = React.createContext(null);

function Test (props) {
  const [state, dispatch] = useReducer(reducer, {
    isFetching: false,
    index: props.location.state.index || 1
  });

  useEffect(() => {
    // ...
  });

  const fetch = () => {
    dispatch({ type: 'FETCH' });
  };

  const confirmClick = () => {
    dispatch({ type: 'DOWN' });
  };

  const goOtherPage = () => {
    props.history.push({
      pathname: 'otherPage',
      state: {
        index: state.index
      }
    });
  };

  const reset = () => {
    dispatch({ type: 'RESET' });
  };

  return (
    <FetContext.Provider value={dispatch}>
      <InputItem value={state.index} />
      <DeepChild {...state} />
      <Button onClick={confirmClick} type='warning'>Parent Reduce</Button>
      <br />
      <Button type='warning' onClick={fetch}>Parent Request!</Button>
      <br />
      <Button type='primary' onClick={reset}>Reset Index</Button>
      <br />
      <Button type='ghost' onClick={goOtherPage}>Next Page</Button>
    </FetContext.Provider>
  );
}

export default Test;
           

OtherPage/index.jsx

/* eslint-disable react/prop-types */
import React, { useReducer, useEffect } from 'react';
import reducer from '../Test/reducer';
import { InputItem, Button } from 'antd-mobile';

function OtherPage (props) {
  const [state, dispatch] = useReducer(reducer, props.location.state);

  useEffect(() => {
    console.log('OtherPage props', state);
  });

  const add = () => {
    dispatch({ type: 'ADD' });
  };

  const goBack = () => {
    console.log('123');
    console.log('props', props);
    // props.history.go(-1);
    props.history.replace({
      pathname: 'Test',
      state: {
        index: state.index
      }
    });
  };

  return (
    <div>
      <InputItem value={state.index} />
      <Button onClick={add} type='primary'> add</Button>
      <br />
      <Button onClick={goBack} type='ghost'> Go Back</Button>
    </div>
  );
}

export default OtherPage;
           

注意點

  • useEffect()

    可以看做是

    class

    寫法的

    componentDidMount

    componentDidUpdate

    以及

    componentWillUnMount

    三個鈎子函數的組合。
    • 當傳回了一個函數的時候,這個函數就在

      compnentWillUnMount

      生命周期調用
    • 預設地,傳給

      useEffect

      的第一個參數會在每次(包含第一次)資料更新時重新調用
    • 當給

      useEffect()

      傳入了第二個參數(數組類型)的時候,

      effect

      函數會在第一次渲染時調用,其餘僅當數組中的任一進制素發生改變時才會調用。這相當于我們控制了元件的

      update

      生命周期
    • useEffect()

      第二個數組為空則意味着僅在

      componentDidMount

      周期執行一次

繼續閱讀