標題有點引戰,但是如果想要跨元件處理狀態,並且狀態非常複雜的話,那麼 useState 可能就不是那麼好管理,也許可以試試看 useReducer。

使用情境

之前有寫過 useState 的介紹文章,裡面的範例是一個點擊器,可以透過點擊不同按鈕去反應結果,這篇就來使用 useReducer 來做出一樣的成果。

初始設置

首先將會分成 3 個元件,分別是加按鈕、減按鈕,還有計算結果,這邊計算結果是用 input 欄位,可以讓使用者自行輸入。

// 減
const MinusCount = () => {
  return <button type="button">-</button>;
};

export default MinusCount;

// 加
const PlusCount = () => {
  return <button type="button">+</button>;
};

export default PlusCount;

// 結果
const Result = () => {
  return <input type="number" value={0} />;
};

export default Result;

建立預設狀態

const initialState = {
  countNumber: 0
}

建立邏輯判斷區來操作狀態

const reducer = (state, action) => {
  switch (action.type) {
    case 'PLUS':
      return {
        ...state,
        countNumber: state.countNumber + 1
      }
    case 'MINUS':
      return {
        ...state,
        countNumber: state.countNumber - 1
      }
    case 'UPDATE_NUMBER':
      return {
        ...state,
        countNumber: action.payload
      }
    default:
      return state
  }
}

使用 useReducer 來帶入狀態以及邏輯判斷

const [state, dispatch] = useReducer(reducer, initialState)

這樣大致上就設置好 useReducer 了,接着就可以將狀態以及方法分別給各自的元件。

<div className='App'>
  <MinusCount method={() => dispatch({ type: 'MINUS' })} />
  <Result num={state.countNumber} />
  <PlusCount method={() => dispatch({ type: 'PLUS' })} />
</div>
// == Result ==
const Result = () => {
  const { state, dispatch } = useContext(AppContext)

  return (
    <input
      type='number'
      value={state.countNumber}
      onChange={(e) =>
        dispatch({ type: 'UPDATE_NUMBER', payload: Number(e.target.value) })
      }
    />
  )
}

// == Plus ==
const PlusCount = ({ method }) => {
  const { dispatch } = useContext(AppContext)

  return (
    <button type='button' onClick={method}>
      +
    </button>
  )
}

// == Minus ==
const MinusCount = ({ method }) => {
  const { dispatch } = useContext(AppContext)

  return (
    <button type='button' onClick={method}>
      -
    </button>
  )
}

搭配使用 useContext

剛剛是使用 props 來傳送,我自己是比較常搭配 useContext 來使用,成果如下 ⬇️

後記

在官方文件中提到 useReducer 是這樣說的:

當你需要複雜的 state 邏輯而且包括多個子數值或下一個 state 依賴之前的 state,useReducer 會比 useState 更適用。而且 useReducer 可以讓你觸發深層更新的 component 作效能的最佳化,因為你可以傳遞 dispatch 而不是 callback。

所以如果是比較單純的狀態的話,那麼使用 useState 就可以了,但是我最近是比較常使用 useReducer,因為寫起來蠻像 Redux 的,而且 useReducer 都把邏輯管理在一起,看起來比較容易閱讀,也方便維護(我就做過把之前的 code 給重寫,看起來就很舒服 XD),所以蠻推薦這個 hook👏。