React Hooks 之 useReducer

370 阅读2分钟

对于复杂的state操作逻辑,嵌套的state对象,推荐使用useReducer

使用

const [state, dispatch] = useReducer(reducer, initState);

userReducer接收两个参数,一个是reducer函数,第二个是初始化state。

useReducer返回值为最新的state和dispath,state是返回返回状态中的值,而dispatch是一个可以发布时间来更新state的方法。

reducer接受两个参数,一个是state另一个是action。

reducer是一个利用action提供的信息,将state从A转换到B的一个纯函数,具有以下几个特点

  • 语法:(state, action) => newState
  • Immutable:每次都返回一个newState,永远不要直接修改state对象
  • Action:一个常规的Action对象通常有type和payload(可选)组成
  • type:本次操作的类型,也是reducer条件判断的依据
  • payload:提供操作附带的数据信息

简单使用例子:

function UseReducerDemo () {
  // 定义初始的state
  const initState = { count: 0 }
  // 第一个参数reducer
  function reducer (state, action) {
    switch (action.type) {
      case 'add':
        return { count: state.count + 1 };
      case 'sub':
        return { count: state.count - 1 };
      default:
        throw new Error()
    }
  }
  // 返回新的state
  const [state, dispatch] = useReducer(reducer, initState)
  return (
    <div>
      <div>count: {state.count}</div>
      <button onClick={() => dispatch({ type: 'add' })}>add</button>
      <button onClick={() => dispatch({ type: 'sub' })}>sub</button>
    </div>
  );
}

一个简单的例子对比useState和useReducer:

useState:

import React, { useState } from 'react';
import submitInfo from './api';
function UseStateDemo () {
  const [name, setName] = useState('')
  const [sex, setSex] = useState(0)
  const [age, setAge] = useState(0)
  const [avatar, setAvatar] = useState('')
  const [interests, setInterests] = useState([])
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState('')
  const submit = (event) => {
    event.preventDefault();
    setError('');
    setLoading(true);
    const param = {
      name,
      sex,
      age,
      avatar,
      interests
    }
    submitInfo({ param })
      .then(() => {
        setLoading(false);
      })
      .catch((error) => {
        setError(error.message);
        setName('');
        setSex(0);
        setAge(0);
        setAvatar('');
        setInterests([])
        setLoading(false);
      });
  }
  const reset = () => {
    setName('');
    setSex(0);
    setAge(0);
    setAvatar('');
    setInterests([])
    setLoading(false);
  }
  return (
    <div>
      {/* jsx demo... */}
      <button onClick={() => submit()}></button>
      <button onClick={() => reset()}></button>
    </div>
  );
}
export default UseStateDemo;

useReducer:

import React, { useReducer } from 'react';
import submitInfo from './api'
function UseReducerDemo () {
  const initialState = {
    name: '',
    sex: 0,
    age: 0,
    avatar: '',
    interests: [],
    loading: false,
    error: ''
  }
  const reducer = (state, action) => {
    switch (action.type) {
      case 'submit':
        return { ...state, loading: false };
      case 'reset':
        return {
          ...state,
          name: '',
          sex: 0,
          age: 0,
          avatar: '',
          interests: []
        };
      default:
        return state
    }
  }
  const [state, dispatch] = useReducer(reducer, initialState)
  const submit = (event) => {
    event.preventDefault();
    const {
      name,
      sex,
      age,
      avatar,
      interests
    } = state
    const param = {
      name,
      sex,
      age,
      avatar,
      interests
    }
    submitInfo({ param })
      .then(() => {
        dispatch('submit')
      })
      .catch((error) => {
        dispatch('reset')
      });
  }
  return (
    <div>
      {/* jsx demo... */}
      <button onClick={() => submit()}></button>
      <button onClick={() => dispatch('reset')}></button>
    </div>
  );
}
export default UseReducerDemo;

相比之下,useReducer使代码更具有可读性,state更新逻辑更清晰。 适合的使用场景:

  • 如果state是一个数组或者对象
  • 如果state变化很复杂,经常一个操作需要修改很多state
  • 如果你希望构建自动化测试用例来保证程序的稳定性
  • 如果你需要在深层子组件里面去修改一些状态(关于这点我们下篇文章会详细介绍)
  • 如果你用应用程序比较大,希望UI和业务能够分开维护

参考(zhuanlan.zhihu.com/p/69622832)