準備
Create React App でプロジェクトディレクトリを作成。
$ npx create-react-app my-todo$ cd my-todo$ npm startUnstated Next をインストール
$ npm install --save unstated-next今回使用するバージョンは下記の通りです。
“react”: “^16.8.6”,
“unstated-next”: “^1.0.1”
コンテナの作成
コンテナは共通で使用するStateや関数を入れます。
最後にオブジェクトで返します。
import { useState } from 'react';
export default () => { const [count, setCount] = useState(1); const [items, setItems] = useState([]);
const handleAdd = (e) => { e.preventDefault();
const newItems = [...items, { id: count, title: e.target.title.value, done: false }];
setItems(newItems); setCount(count + 1);
e.target.title.value = ''; }
const handleDone = (item) => { const newItems = [...items]; let index = newItems.indexOf(item); item.done = !item.done; newItems[index] = item;
setItems(newItems); }
return { count, items, handleAdd, handleDone };}useStateを使うとthis.stateのような書き方をしなくていいのでシンプルに書けますね。
Task.js
中心となるコンポーネントです。
createContainerをexportして子のコンポーネントで共通のコンテナを使用できるようにします。
コンテナを使用するコンポーネントをTaskContainer.Providerで囲みます。
import React from 'react';import { createContainer } from "unstated-next";import useTask from './TaskContainer';import TaskList from './TaskList';import TaskInput from './TaskInput';
export const TaskContainer = createContainer(useTask);
export default () => { return ( <TaskContainer.Provider> <TaskInput /> <TaskList /> </TaskContainer.Provider> );};TaskInput.js
入力用コンポーネントです。
TaskContainerを読み込んでuseContainerすると、container.handleAddのような形で、リターンしたステートや関数にアクセスできます。
import React from 'react';import { TaskContainer } from './Task';
export default () => { const container = TaskContainer.useContainer();
return( <form onSubmit={container.handleAdd} className="task-input"> <input type="text" name="title" /> <button>追加</button> </form> );};TaskList.js
一覧表示用のコンポーネントです。
TaskInput.jsとやってることは変わらないですね。
import React from 'react';import { TaskContainer } from './Task';
export default () => { const container = TaskContainer.useContainer();
return( <ul> { container.items.map((item) => { return( <li key={item.id} onClick={() => container.handleDone(item)} className={`${item.done ? "done" : ""}`} >{item.title}</li> ); }) } </ul> );};App.js
App.jsで起点となるTaskコンポーネントを配置します。
import React from 'react';import Task from './components/Task'import './App.css';
function App() { return ( <div className="App"> <Task /> </div> );}
export default App;最後にスタイルを作って完成です。
今回はdoneクラスが付与されたとき、打ち消し線が引かれるという簡易的なものです。
.done { text-decoration: line-through;}以上。
React Hooks と Unstated Next の組み合わせでかなりシンプルに書けるようになる気がします。
