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方案_第1张图片

├── 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`操作

效果图

实现

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写法的componentDidMountcomponentDidUpdate以及componentWillUnMount三个钩子函数的组合。
    • 当返回了一个函数的时候,这个函数就在compnentWillUnMount生命周期调用
    • 默认地,传给useEffect的第一个参数会在每次(包含第一次)数据更新时重新调用
    • 当给useEffect()传入了第二个参数(数组类型)的时候,effect函数会在第一次渲染时调用,其余仅当数组中的任一元素发生改变时才会调用。这相当于我们控制了组件的update生命周期
    • useEffect()第二个数组为空则意味着仅在componentDidMount周期执行一次

你可能感兴趣的:(前端,JavaScript,React,React,Hooks,Redux)