[React] When to useReducer instead of useState

useState is typically simpler at first than useReducer (you can even implement useState using useReducer), but there's one scenario where useReducer is almost certainly better than useState:

When one element of your state relies on the value of another element of your state in order to update: useReducer

More information: KcD's blog

 

import React from 'react'
import ReactDOM from 'react-dom'

function undoReducer(state, action) {
  const {past, present, future} = state
  const {type, newPresent} = action

  switch (type) {
    case 'UNDO': {
      if (past.length === 0) return state

      const previous = past[past.length - 1]
      const newPast = past.slice(0, past.length - 1)
      return {
        past: newPast,
        present: previous,
        future: [present, ...future],
      }
    }
    case 'REDO': {
      if (future.length === 0) return state
      const next = future[0]
      const newFuture = future.slice(1)
      return {
        past: [...past, present],
        present: next,
        future: newFuture,
      }
    }
    case 'SET': {
      if (newPresent === present) {
        return state
      }
      return {
        past: [...past, present],
        present: newPresent,
        future: [],
      }
    }
    case 'RESET': {
      return {
        past: [],
        present: newPresent,
        future: [],
      }
    }
    default: {
      throw new Error(`Unhandled action type: ${type}`)
    }
  }
}

function useUndo(initialPresent) {
  const [state, dispatch] = React.useReducer(undoReducer, {
    past: [],
    present: initialPresent,
    future: [],
  })

  const canUndo = state.past.length !== 0
  const canRedo = state.future.length !== 0

  const undo = React.useCallback(() => {
    dispatch({type: 'UNDO'})
  }, [])

  const redo = React.useCallback(() => {
    dispatch({type: 'REDO'})
  }, [])

  const set = React.useCallback(newPresent => {
    dispatch({type: 'SET', newPresent})
  }, [])

  const reset = React.useCallback(newPresent => {
    dispatch({type: 'RESET', newPresent})
  }, [])

  return [state, {set, reset, undo, redo, canUndo, canRedo}]
}

function App() {
  const [state, {set}] = useUndo('first')

  React.useEffect(() => {
    set('second')
  }, [set])

  React.useEffect(() => {
    set('third')
  }, [set])

  return <pre>{JSON.stringify(state, null, 2)}</pre>
}

ReactDOM.render(<App />, document.getElementById('root'))

 

上一篇:篇11-$past构造与带时钟门控的$past构造


下一篇:模块“DevicePowerOn”启动失败