Next.jsでtodoアプリ を作ろうとしたらRecoil + React Hook Form v7で楽ができた件
はじめに
なろう形式のタイトルってわかりやすいですね。
初投稿です。
Reactの学習のため、Todoアプリを作成しました。
どうせなら最新のライブラリを使用しようとRecoil, React Hook Form v7を使ってみましたので、備忘録がてらその内容を紹介します。
環境
Windows10 64bit(20H2)
WSL(Ubuntu 18.) v1
node v14.17.1
yarn 1.22.5
VSCode
完成図
こんな感じの簡単なtodoアプリを作ってみました。
見た目は完全に度外視しています。
install
まずWSLで下記コマンドを実行して、Next.js(TypeScript)のプロジェクトを作成します。
$ yarn create next-app --typescript
// 対話形式でプロジェクト名を聞かれるので、任意の名前を入力します。
What is your project named? … next-recil-todo
プロジェクトの中身はこのようになっています。
$ ls -1 next-recil-todo
README.md
next-env.d.ts
next.config.js
node_modules
package.json
pages
public
styles
tsconfig.json
yarn.lock
インストールできたので、動作確認します。
$ cd next-recil-todo
$ yarn dev
この画面が出ていれば成功です。
続いて使用するライブラリをインストールします。
$ yarn add recoil react-hook-form moment
これでインストールは完了です。
実装
コードはgithubに上げましたので、全体が見たい方は参照してください。
Qiita
atoms
まずはRecoilで使用するatomのファイルを配置するディレクトリを作成します。
$ mkdir -p src/atoms
作成したディレクトリに下記のファイルを作成します。
import { atom } from "recoil";
export type Todo= {
id: string
title: string
text: string
isComplete: boolean
}
// Todoリストを保持
const todoListState = atom<Todo[]>({
key: 'todoListState',
default: [],
});
export { todoListState }
components
次にcomponetを管理するディレクトリを作成します。
$ mkdir src/components
作成したディレクトリに下記2つのファイルを作成します。
React Hook Form v7でRecoilを使用する際、validationを指定するには「 {...register(~」と記述する必要があります。
React Hook Form v6でRecoilでサンプルを記載しているサイトが多かったので、React Hook FormのVersionに注意してください。
import { useSetRecoilState } from "recoil";
import { todoListState } from "../atoms/states";
import moment from "moment";
import { useForm } from 'react-hook-form'
type FormData = {
id: string,
title: string,
text: string,
isComplete: boolean,
}
const Edit: React.FC = () => {
const setTodoList = useSetRecoilState(todoListState);
// react-hook-formを設定
const { register, handleSubmit, formState: { errors }, reset } = useForm<FormData>({
mode: 'onChange',
defaultValues: {
id : moment().format('YYYYMMDDHHmmss'),
title: '',
text: '',
isComplete: false,
}
})
// Todoを追加
const addTodo = (data: FormData) => {
setTodoList((oldTodoList) => [
...oldTodoList,
data,
]);
reset()
}
return (
<div>
<form onSubmit={handleSubmit(addTodo)}>
<label>title:</label>
{/* 入力要素とvalidationを設定 */}
<input
type="text"
{...register(
"title",
{
required: '必須項目です',
maxLength: {
value: 20,
message: '20文字以内で入力してください',
},
}
)}
/>
{errors.title && <span>{errors.title.message}</span>}
<br />
<label>text:</label>
{/* 入力要素とvalidationを設定 */}
<input
type="text"
{...register(
"text",
{
required: '必須項目です',
maxLength: {
value: 20,
message: '20文字以内で入力してください',
},
}
)} />
{errors.text && <span className="error.main">{errors.text.message}</span>}
<br />
<button type="submit">送信</button>
</form>
</div>
)
}
export default Edit
import { useRecoilState, useSetRecoilState } from "recoil";
import { Todo, todoListState } from "../atoms/states";
const TodoList = () => {
const [todoList, setTodoList] = useRecoilState(todoListState)
// 完了したTodoを削除
const deleteTodo = (id:string) => {
const target = todoList.filter((todo) => {
return (todo.id != id)
})
setTodoList(target);
}
return (
<div>
{todoList.map(item =>
<div key={item.id}>
タイトル:{item.title} <br />
テキスト:{item.text} <br />
<button onClick={() => { deleteTodo(item.id) }} >
完了
</button>
</div>
)}
</div>
)
}
export default TodoList
Recoilを使用したcomponetを表示します。
import React from 'react';
import type { AppProps } from 'next/app'
import { RecoilRoot } from 'recoil';
function MyApp({ Component, pageProps }: AppProps) {
return (
// RecoilRootを設定
<RecoilRoot>
<Component {...pageProps} />
</RecoilRoot>
)
}
export default MyApp
import TodoList from '../src/components/list'
import Edit from '../src/components/edit'
export default function Home() {
return (
<div>
{/* componetを表示 */}
<Edit />
<TodoList />
</div>
)
}
まとめ
Reactの書籍ではReduxを使用していましたが分かりづらいのもあり、Recoilを使ってみたところ非常に簡単で使いやすかったです。
これからはもっと発展させてGraphQLとの連携を行ってみようと思います。
Discussion
legend-state x React Hook Formでトライしてみました