React Reduxに相当するものをhooksでやってみる


React Reduxに相当するものをhooksでやってみる

Reduxを使わない場合にそれに相当するやつをHooksで実現したいなーって時に考えたやつ

とりあえず完成したやつ



// TODOの型
interface ITodo {
  id: number
  body: string
  isDone: boolean
}

// TODO検索用の型
interface ISearchTodoParams {
  body?: string
}

// TODO検索のAPIの代わり
const mockApi = (params?: ISearchTodoParams): Promise<{ todos: ITodo[] }> => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({
        todos: [
          { id: 1, body: '卵買う', isDone: true },
          { id: 2, body: '塩買う', isDone: false },
          { id: 3, body: '家買う', isDone: false },
        ],
      })
    }, 1000)
  })
}

// メインディッシュ
export const useTodos = () => {
  const [todos, setTodos] = useState<ITodo[]>([])
  const [isLoading, setLoading] = useState(false)
  const [isError, setError] = useState(false)

  const fetchTodos = (params?: ISearchTodoParams) => {
    setLoading(true)

    // apiの代わり
    mockApi(params)
      .then(response => {
        setTodos(response.todos)
      })
      .catch(() => {
        setError(true)
      })
      .finally(() => {
        setLoading(false)
      })
  }

  return { todos, fetchTodos, isLoading, isError }
}

// 使うとき
export const Todos: FC = () => {
  const { todos, fetchTodos } = useTodos()

  // 初回取得
  useEffect(() => {
    fetchTodos()
  }, [])

  return (
    <ul>
      <input
        type="text"
        onChange={e => {
          const val = e.target.value
          if (val.length === 0) {
            return
          }

          fetchTodos({ body: val })
        }}
      />

      {todos.map(todo => (
        <li>{todo.body}</li>
      ))}
    </ul>
  )
}

解説

使うときはこの辺の引数を受け取って使える感じ

/**
 * todos: TODOの配列が入ったやつ
 * fetchTodos: todoの一覧を取得する関数
 * isError: エラーが起きたらtrueになるやつ
 * isLoading: 通信中にtrueになるやつ
 */
const { todos, fetchTodos, isError, isLoading } = useTodos()

reducers や actionsとかを書くのと比べると記述量が少なくなってる
Componentが離れたところで参照させたいとかがなければ、これくらいでいいのかなと
reducersやactionを

以上!