準備
Create React App でプロジェクトディレクトリを作成。
$ npx create-react-app my-todo $ cd my-todo $ npm start
Unstated Next をインストール
$ npm install --save unstated-next
今回使用するバージョンは下記の通りです。
“react”: “^16.8.6”,
“unstated-next”: “^1.0.1”
コンテナの作成
コンテナは共通で使用するStateや関数を入れます。
最後にオブジェクトで返します。
src/components/TaskContainer.js
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
で囲みます。
src/components/Task.js
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
のような形で、リターンしたステートや関数にアクセスできます。
src/components/TaskInput.js
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
とやってることは変わらないですね。
src/components/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
コンポーネントを配置します。
src/App.js
import React from 'react'; import Task from './components/Task' import './App.css'; function App() { return ( <div className="App"> <Task /> </div> ); } export default App;
最後にスタイルを作って完成です。
今回はdoneクラスが付与されたとき、打ち消し線が引かれるという簡易的なものです。
src/App.css
.done { text-decoration: line-through; }
以上。
React Hooks と Unstated Next の組み合わせでかなりシンプルに書けるようになる気がします。
jamiebuilds/unstated-next