5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

NextremerAdvent Calendar 2018

Day 7

ReactHooksの使い方について

Last updated at Posted at 2018-12-06

この記事はNextremer Advent Calendar 2018の7日目の記事です。

(億番煎じぐらいですが)ReactHooks触ってみて使い方について検討したのでまとめます。
React Hooksが提供する機能の説明とかは他に良記事がたくさんあるので割愛。

TL;DR

結局ReduxライクなReducerによる管理が楽そう。(ならそれReduxでよくね?とか言わない)
作ったものはこちら: https://github.com/k-tada/react-hooks-todos

State管理について

Redux等を使わない、ReactのみでState管理する方法について検討してみた。
多分Redux使わなくなることは無いと思うけど、ReactでState管理する方が楽になるならそっちの方がいいので。

1Component内で完結するような小規模なStateの管理は今回の検討スコープから外してます。

というか検討するまでも無くuseState使えばOKかと。

基本はContext + Reducer

ReactでコンポーネントをまたいでState管理をする場合はやっぱりContextを使うのが楽そう。
Stateの更新にはReactのuseReducerを使うのが良さそう。
ってことで、Context + Reducerでモジュールを管理するのが一番しっくり来るかなと思ったので実装してみた。

どんどんReduxの設計に寄っていく。。。それReduxでよくね?

例えばTodoListの管理をするとして、作るならこんな感じになりそう

const TodoContext = createContext({ todos: [] })

function Form() {
  const { dispatch } = useContext(TodoContext)
  // 略
  return (
    <div>
      <input type="text" ... onKeyUp={e => e.keycode==13 && dispatch( ... ) } />
    </div>
  );
}

function List() {
  const { state } = useContext(TodoContext)
  return (
    <div>
      {state.todos.map(todo => <div>{ todo }</div>)}
    </div>
  )
}

function Main() {
  const reducer = (state, action) => { /* 略 */ }
  const [state, dispatch] = useReducer(reducer, useContext(TodoContext))
  return (
    <TodoContext.Provider value={{ state, dispatch }}>
      <Form />
      <List />
    </TodoContext.Provider>
  )
}

複数のContextを使う場合、Providerを重ねがけするイメージ。

return (
  <HogeContext.Provider value={{ state, dispatch }}>
    <FugaContext.Provider value={{ state, dispatch }}>
      <App />
    </FugaContext.Provider>
  </HogeContext.Provider>
)

Ducksパターン的なアレ

ここで、ContextごとにReducerを用意することになるので、Ducksパターンよろしく1ファイルにまとめちゃいたい。
ということでこうしてみた。

stores/todos.js
import React, {
  createContext,
  useContext,
  useReducer,
} from "react"

export const Store = createContext({ todos: [] })

const reducer = (state, action) => {
  switch (action.type) {
    // 略
  }
}

export const createStore = () => {
  const [state, dispatch] = useReducer(reducer, useContext(Store))
  return { state, dispatch }
}

使う際はこんな感じ

Main.js
import {
  Store as TodosStore,
  createStore as createTodosStore
} from './stores/todos';

function Main() {
  return (
    <TodosStore.Provider value={createTodosStore()}>
      <Form />
      <List />
    </TodosStore.Provider>
  );
}
components/Form.js
import { Store as TodosStore } from "../stores/todos";

function Form() {
  const { dispatch } = useContext(TodosStore)
  // 略
  return (
    <div>
      <input type="text" ... onKeyUp={e => e.keycode==13 && dispatch( ... ) } />
    </div>
  );
}
components/List.js
import { Store as TodosStore } from "../stores/todos";

function List() {
  const { state } = useContext(TodosStore)
  return (
    <div>
      {state.todos.map(todo => <div>{ todo }</div>)}
    </div>
  )
}

まとめ

ReactHooksを使うと多少大きめのStateも扱いやすくなりそう。
ただReduxと併用すると確実に混乱する未来が見えるので、この仕組みを取るならMobXとかと併用する方がいいのかも知れない。

いずれにせよ、ReactHooks自体はまだRFCの段階で、これから破壊的な変更が無いとも限らないので、
しばらくはReactの動向を監視しつつ、従来のClass Componentやrecompose + SFCのまま運用するのが良さそう。

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?