関数コンポーネント、ReactHooksについて調べ、触ってみてわかったことをまとめます。
背景
- 以前はクラスコンポーネントが主流だった
- クラスコンポーネントはいろんなことができる故に、コードがカオスになりがち
- 現在はより関数コンポーネントが主流
- 関数コンポーネントとHooksを組み合わせることで、程よくReactの機能にアクセスできるようになる
注意点など
- Hooksは全て
useHogehoge
という名前 - 必ず関数の中のトップレベルで使う
- 要はif文やfor文の中で使ってはいけない
- いつ何時でも、同じ回数同じ順番で実行される必要がある。
- 基本的に関数の一番上に並べておけばOK
よく使うHooks
-
useState
- stateの読み書きに使う
-
useEffect
- ライフサイクルメソッドの代替
関数コンポーネント + HooksでTodoアプリを作ってみる
セットアップ
$ create-react-app react-todo
$ cd react-todo
$ rm -rf src
$ mkdir src src/components
$ cd src
$ touch index.js components/App.js components/AddTodo.js components/TodoElement.js
実装
src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
コンポーネントを分割しない場合
src/components/App.js
import React, { useState } from "react";
const App = () => {
const [value, setValue] = useState("");
const [todoList, setTodoList] = useState([]);
const handleChange = (e) => {
const newValue = e.target.value;
setValue(newValue);
};
const add = () => {
const newTodo = { id: todoList.length, content: value };
const newTodoList = [...todoList, newTodo];
setTodoList(newTodoList);
setValue("");
};
const handleDelete = (id) => {
const newTodoList = todoList.filter((todo) => todo.id !== id);
setTodoList(newTodoList);
};
return (
<div>
<h1>TODO App</h1>
<input type="text" value={value} onChange={handleChange} />
<button onClick={add}>追加</button>
<ul>
{todoList.map((todo) => (
<li key={todo.id}>
{todo.content}
<button onClick={() => handleDelete(todo.id)}>削除</button>
</li>
))}
</ul>
</div>
);
};
export default App;
コンポーネントを分割する場合
src/components/App.js
import React, { useState } from "react";
import TodoElement from "./TodoElement";
import AddTodo from "./AddTodo";
const App = () => {
const [value, setValue] = useState("");
const [todoList, setTodoList] = useState([]);
const handleChange = (e) => {
const newValue = e.target.value;
setValue(newValue);
};
const add = () => {
const newTodo = { id: todoList.length, content: value };
const newTodoList = [...todoList, newTodo];
setTodoList(newTodoList);
setValue("");
};
const handleDelete = (id) => {
const newTodoList = todoList.filter((todo) => todo.id !== id);
setTodoList(newTodoList);
};
return (
<div>
<h1>TODO App</h1>
<AddTodo value={value} onChange={handleChange} add={add} />
<ul>
{todoList.map((todo) => (
<TodoElement
key={todo.id}
content={todo.content}
onDelete={() => handleDelete(todo.id)}
/>
))}
</ul>
</div>
);
};
export default App;
src/components/AddTodo.js
import React from "react";
const AddTodo = (props) => {
return (
<>
<input type="text" value={props.value} onChange={props.onChange} />
<button onClick={props.add}>追加</button>
</>
);
};
export default AddTodo;
src/components/TodoElement.js
import React from "react";
const TodoElement = (props) => {
return (
<li>
{props.content}
<button onClick={props.onDelete}>削除</button>
</li>
);
};
export default TodoElement;
ポイント
- render関数はpureな関数であるべき
- stateの変更が起こるとrender関数が呼ばれるようになっているので無限ループになってしまう
- render関数内でstateを変更してはいけない
- 関数コンポーネントで実装すると、thisを使って内部のメソッドや変数にアクセスする必要がないので、thisの挙動を意識せずに済む
-
onChange
などのイベントハンドラでは、関数を渡すと引数も勝手に渡してくれる -
state
はできるだけ一箇所で管理する- 子コンポーネント内で変更があったときはpropsの関数を実行して親で処理を行う
- propsは
on〇〇
という名前が一般的なのに対して、そのコールバック関数はhandle〇〇
という名前が一般的