「JavaScriptの関数の書き方には種類があるけど使い分けがわかっていない」
「同じようなプログラムでもググって出てくるサンプルコードが微妙に違うのが謎に感じる」
「コンポーネントで関数を呼び出すのに上手くいかないことがある」
本日はReactにおけるコンポーネントで関数を呼ぶ書き方について解説していきます。
そもそもJavaScriptの関数が複数の書き方ができるため初学者の方は迷われるのだと思います。
Reactと言っても結局はJavaScriptなので、今回の内容でJavaScriptの基礎に自信がなかった方にも役立つはずです。
また動画も用意しているので必要な方は以下よりご覧ください。
Reactのコンポーネントで関数を呼ぶ書き方
基本的には3つの書き方があることを頭に入れておけば大丈夫です。
①「関数名」
②「関数名( )」
③「( ) => 関数名 ( )」
それぞれは関数を使用する状況によって使い分けるのもので、どれかが一番正しいということではありません。
JavaScriptの基本的な文法を触ったことがある方なら分かると思うのですが、引数や戻り値の指定によって関数の使い方が変わってきますよね。
Reactにおいてコンポーネントで関数を呼ぶときも、同じように引数と戻り値をどうするか?ということを考えていきます。
今回は例題として以下のような簡単なTODOアプリを使って細かく説明していきます。
import "./App.css";
import FetchApi from "./screens/sample/Todo";
function App() {
return (
<div className="App">
<Todo />
</div>
);
}
export default App;
import React, { useState } from "react";
const Todo = () => {
const [inputText, setInputText] = useState("");
const [taskList, setTaskList] = useState([]);
const handleChange = (e) => {
setInputText(e.target.value);
};
const addTask = (e) => {
e.preventDefault();
setTaskList([
...taskList,
{
id: crypto.randomUUID(),
text: inputText,
},
]);
setInputText("");
};
const handleDelete = (id) => {
let array = taskList.filter((task) => task.id !== id);
setTaskList(array);
};
return (
<>
<form>
<input type="text" onChange={handleChange} value={inputText} />
<button onClick={(e) => addTask(e)}>追加</button>
</form>
<div>
{taskList.map((task) => (
<div key={task.id}>
<div>
{task.text}:{task.id}
<button onClick={() => handleDelete(task.id)}>削除</button>
</div>
</div>
))}
</div>
</>
);
};
export default Todo;
最初の画面は以下のような入力欄と追加ボタンのみが表示されます。
入力欄にタスクを入力して追加ボタンをクリックするとタスクが追加されます。
また追加されたタスクには削除ボタンが一緒に表示されて、削除ボタンをクリックすると画面から消えるものです。
今回のコンポーネントで定義した関数は以下の3つです。
入力欄のテキストを取得する関数:handleChange
タスクを追加する関数:addTask
タスクを削除する関数:handleDelete
それぞれコンポーネント内の必要な場所でonClickイベント、onChangeイベントで呼び出します。
import React, { useState } from "react";
const Todo = () => {
const [inputText, setInputText] = useState("");
const [taskList, setTaskList] = useState([]);
// 入力欄のテキストを取得する関数
const handleChange = (e) => {
setInputText(e.target.value);
};
// タスクを追加する関数
const addTask = (e) => {
e.preventDefault();
setTaskList([
...taskList,
{
id: crypto.randomUUID(),
text: inputText,
},
]);
setInputText("");
};
// タスクを削除する関数
const handleDelete = (id) => {
let array = taskList.filter((task) => task.id !== id);
setTaskList(array);
};
return (
<>
<form>
<input type="text" onChange={handleChange} value={inputText} />
<button onClick={(e) => addTask(e)}>追加</button>
</form>
<div>
{taskList.map((task) => (
<div key={task.id}>
<div>
{task.text}:{task.id}
<button onClick={() => handleDelete(task.id)}>削除</button>
</div>
</div>
))}
</div>
</>
);
};
export default Todo;
基本的には「<input type=”text” onChange={handleChange} value={inputText} />」のように定義した関数名をイベントに指定します。
それぞれの関数は実行した戻り値を「const handleDelete = 処理内容」のように定数や変数に受け入れるようにしているからです。
またhandleChangeについてはonchageイベントの中で呼び出していて、簡単に言うと「変更があるとき」という意味です。
そのため入力欄に1文字入力するたびにhandleChangeが実行されています。
続いて「<button onClick={(e) => addTask(e)}>追加</button>」を見ていきます。
addTaskはタスクを追加する関数でonClickイベントの中で呼び出しています。
onClickイベントは簡単にいうと「クリックされたとき」という意味です。
タスクの追加はボタンをクリックすることで実現するように作っているためです。
先ほどのhandleChangeと違ってアロー関数で呼び出していますね。
addTaskを定義したときに「e.preventDefault();」を書くために引数にeを指定する必要があります。
そのためonClickイベントでaddTaskを呼び出すときにはeを指定します。
またReactではコンポーネント内のイベントで関数を呼び出す際に第一引数はeであることがデフォルトで設定されているため、「(e) => addTask(e)」のように第一引数、第二引数ともにeを入れています。
import React, { useState } from "react";
const Todo = () => {
const [inputText, setInputText] = useState("");
const [taskList, setTaskList] = useState([]);
const handleChange = (e) => {
setInputText(e.target.value);
};
// eを引数とするように定義した
const addTask = (e) => {
e.preventDefault();
setTaskList([
...taskList,
{
id: crypto.randomUUID(),
text: inputText,
},
]);
setInputText("");
};
const handleDelete = (id) => {
let array = taskList.filter((task) => task.id !== id);
setTaskList(array);
};
return (
<>
<form>
<input type="text" onChange={handleChange} value={inputText} />
{/* Reactでは第一引数にeを入れるのがデフォルト、さらにaddTaskは関数の定義でeを引数に取る */}
<button onClick={(e) => addTask(e)}>追加</button>
</form>
<div>
{taskList.map((task) => (
<div key={task.id}>
<div>
{task.text}:{task.id}
<button onClick={() => handleDelete(task.id)}>削除</button>
</div>
</div>
))}
</div>
</>
);
};
export default Todo;
ちなみに「e.preventDefault();」とはデータの送信を無効にするために書くものです。
通常HTMLのformタグは何かしらデータを送信して画面遷移することを前提に用意されています。
お問い合わせフォームがイメージしやすいでしょう。
しかし今回のTODOアプリではどこかにデータを送信するわけでも画面遷移するわけでもないので「e.preventDefault();」と書く必要がありました。
とはいえformタグを削除すれば不要になります。
import React, { useState } from "react";
const Todo = () => {
const [inputText, setInputText] = useState("");
const [taskList, setTaskList] = useState([]);
const handleChange = (e) => {
setInputText(e.target.value);
};
// ここを修正
const addTask = () => {
// e.preventDefault();を削除した
setTaskList([
...taskList,
{
id: crypto.randomUUID(),
text: inputText,
},
]);
setInputText("");
};
const handleDelete = (id) => {
let array = taskList.filter((task) => task.id !== id);
setTaskList(array);
};
return (
<>
{/* formタグを削除した */}
<input type="text" onChange={handleChange} value={inputText} />
{/* ここを修正 */}
<button onClick={() => addTask()}>追加</button>
<div>
{taskList.map((task) => (
<div key={task.id}>
<div>
{task.text}:{task.id}
<button onClick={() => handleDelete(task.id)}>削除</button>
</div>
</div>
))}
</div>
</>
);
};
export default Todo;
上記のようなコードでも正常に動作します。
少し脱線しましたが引数が絡むので解説しておきました。
最後に「<button onClick={() => handleDelete(task.id)}>削除</button>」を見ていきます。
こちらもaddTaskと同じでボタンをクリックして発動させるのでonClickイベントで関数handleDeleteを呼び出します。
さらに関数handleDeleteはどのタスクを削除するか判別するために引数にタスクのIDを入れることを定義します。
onClickイベントで関数を呼び出すときの第一引数はeになるのですが、eを使わないのであれば省略しても良いようになっています。
そのため第二引数にtask.idでタスクのIDだけ渡しています。
import React, { useState } from "react";
const Todo = () => {
const [inputText, setInputText] = useState("");
const [taskList, setTaskList] = useState([]);
const handleChange = (e) => {
setInputText(e.target.value);
};
const addTask = (e) => {
e.preventDefault();
setTaskList([
...taskList,
{
id: crypto.randomUUID(),
text: inputText,
},
]);
setInputText("");
};
// タスクのIDを引数とするように定義した
const handleDelete = (id) => {
let array = taskList.filter((task) => task.id !== id);
setTaskList(array);
};
return (
<>
<form>
<input type="text" onChange={handleChange} value={inputText} />
<button onClick={(e) => addTask(e)}>追加</button>
</form>
<div>
{taskList.map((task) => (
<div key={task.id}>
<div>
{task.text}:{task.id}
{/* Reactでは第一引数にeを入れるのがデフォルトだがeは不要なので、第二引数のみ指定 */}
<button onClick={() => handleDelete(task.id)}>削除</button>
</div>
</div>
))}
</div>
</>
);
};
export default Todo;
ちなみに関数を呼び出すときにアロー関数で書く理由ですが、ボタンをクリックした時のみに関数を実行するためです。
例えばhandleDeleteを以下のように「handleDelete(task.id)」と書くと正常に動作しません。
イベント内で「関数名( )」としたとき、画面がロードしたときにすぐに関数を実行することになるからです。
画面がロードしたときにすぐに実行するような状況はもちろんありますが、今回のTODOアプリについてhandleDeleteは画面がロードしたときに実行するわけではないですよね。
そのためアロー関数にしないといけないわけです。
import React, { useState } from "react";
const Todo = () => {
const [inputText, setInputText] = useState("");
const [taskList, setTaskList] = useState([]);
const handleChange = (e) => {
setInputText(e.target.value);
};
const addTask = (e) => {
e.preventDefault();
setTaskList([
...taskList,
{
id: crypto.randomUUID(),
text: inputText,
},
]);
setInputText("");
};
const handleDelete = (id) => {
let array = taskList.filter((task) => task.id !== id);
setTaskList(array);
};
return (
<>
<form>
<input type="text" onChange={handleChange} value={inputText} />
<button onClick={(e) => addTask(e)}>追加</button>
</form>
<div>
{taskList.map((task) => (
<div key={task.id}>
<div>
{task.text}:{task.id}
{/* これだと画面がロードしてすぐに実行することになり今回の要件では適さない */}
<button onClick={handleDelete(task.id)}>削除</button>
</div>
</div>
))}
</div>
</>
);
};
export default Todo;
そもそもhandleChangeのように引数が不要な関数を定義しているのであれば、アロー関数にする必要はなく「関数名」だけ書けばOKです。
なかなか初学者の方には難しい内容かもしれませんね。
状況によって適する書き方が変わるので、実務を通して体感するのが一番かもしれません。
今は理解できなかったとしても、ポートフォリオ作成などの実務のときにもう一度この記事を見返してみてくださいね。
また今回参考にした書籍は以下になりますので興味のある方はご覧ください