準備
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
