😸

【React】オブジェクト型にはuseReducerが便利

2022/09/25に公開

オブジェクト型の管理

「useStateでオブジェクト型管理するのめんどくさいな・・・」
「プロパティ一斉更新ならまだ良いんだけど、」
「部分的に更新とかが常時発生するものはいちいちスプレッド構文なりで気を配らないといけない・・・」

const [todo, setTodo] = useState({
  id: 0,
  userId: 0,
  title: '',
  completed: false,
})

...

setTodo((currentTodo) => ({
  ...currentTodo,
  completed: !currentTodo.completed
}))

useReducer使おう

「useReducer使ってみよう」
「Reducerを使うとあらかじめアクションタイプを定義できる」

const initialTodoState = {
  id: 0,
  userId: 0,
  title: '',
  completed: false,
}

const todoReducer = (state, action) => {
  switch (action.type) {
    case 'TOGGLE_COMPLETED':
      return {
        ...state,
        completed: !state.completed,
      }
    default:
      return state
  }
}

const [todo, todoDispatch] = useReducer(todoReducer, initialTodoState)

「stateを変更したいときはこう」
「毎回オブジェクトに気を使ってスプレッド構文書く必要もないし、」
宣言的にstateを変更できてすごくわかりやすくなった!」

todoDispatch({ type: 'TOGGLE_COMPLETED' })

引数をもとにstateを変更する

引数も簡単に渡せる」
「reducerを更新しよう」

const todoReducer = (state, action) => {
  switch (action.type) {
    ...
    case 'CHANGE_TITLE':
      return {
        ...state,
        title: action.payload,
      }
    default:
      return state
  }
}

...

todoDispatch({ type: 'CHANGE_TITLE', payload: 'タイトルを変更する' })

reducerユースケース

「複数のタイプをもつダイアログを管理するのはアクションが決まってて管理しやすい」
「タイプごとに同じようなstate更新処理を書くのは冗長だし、」
「reducerであらかじめ定義しておくことで宣言的ですごくわかりやすくなった!」

const initialDialogState = {
  isShow: false,
  type: 'ADD',
}
const dialogReducer = (state, action) => {
  switch (action.type) {
    case 'OPEN':
      return {
        isShow: true,
        type: action.payload,
      }
    case 'CLOSE':
      return {
        ...state,
        isShow: false,
      }
    default:
      return state
  }
}

const [dialog, dialogDispatch] = useReducer(dialogReducer, initialDialogState)

...

dialogDispatch({ type: 'OPEN', payload: 'ADD' })
dialogDispatch({ type: 'OPEN', payload: 'DELETE' })
dialogDispatch({ type: 'OPEN', payload: 'COPY' })
dialogDispatch({ type: 'OPEN', payload: 'UPDATE' })
dialogDispatch({ type: 'CLOSE' })

...

const renderDialog = useCallback((dialogType) => {
  switch (dialogType) {
    case 'ADD':
      return <AddDialog open={ dialog.isShow } />
    case 'DELETE':
      return <DeleteDialog open={ dialog.isShow } />
    ...
    default:
      return <AddDialog open={dialog.isShow} />
  }
}, [])

...

renderDialog(dialog.type)

Discussion