为什么在React的事件(例如onClick、onChange)的prop中不能写成”函数名()”?
首先
-
- この記事ではReactアプリケーションでイベント(onClick、onChangeなど)のprop(コンポーネントの引数)の正しい渡し方について、サンプルを使いながら紹介します
- 初歩的な内容ですが筆者の中で言語化出来ていなかった為、記事としました
这篇文章的受众是什么类型的人?
-
- 現在Reactを学ばれている方
- これからReactを学ばれる方
行动环境
-
- react-dom@18.2.0
-
- react-scripts@5.0.1
- react@18.2.0
本文中使用的样本
以下示例是当点击“按钮1至按钮3”中的任意一个按钮时,将“0”部分逐渐加一的情况。
在这篇文章中提到的源代码可以通过在CodeSandbox的”App.js”文件中进行复制粘贴来进行确认运行。
那么,让我们进入正题吧。
对于样本,我们将添加以下功能作为额外实现。
-
- ボタンnがクリックされた場合、「ボタンnがクリックされました!」というメッセージを「Hello」と書かれている部分に表示する
- ( 例:ボタン1がクリックされた場合、「Hello」 → 「ボタン1がクリックされました!」
NG模式
我在函数”func1″中添加了参数”n”,并追加了setMessage的处理。为此,我将按钮标签的prop设置为按钮编号作为参数的值。
import { useState } from "react";
export const App = () => {
const [count, setCount] = useState(0);
const [message, setMessage] = useState("Hello");
const func1 = (n) => {
setCount(count + 1);
// messageを更新
setMessage(`ボタン${n}がクリックされました!`);
};
return (
<>
<h1>{count}</h1>
<p>{message}</p>
{/* func1に引数としてボタン番号を渡す */}
<button onClick={func1(1)}>ボタン1</button>
<button onClick={func1(2)}>ボタン2</button>
<button onClick={func1(3)}>ボタン3</button>
</>
);
};

这个错误简洁地表达为。
React君:“这个…一直在无限循环啊!!”
请注意以下部分,它调用了函数“func1”来处理上述的NG模式。
{/* func1に引数としてボタン番号を渡す */}
<button onClick={func1(1)}>ボタン1</button>
以下有两个要点。
-
- 「関数名()」の形式で記述するとコンポーネントのレンダリング時に関数が実行される
- 関数「func1」内ではステート(今回はcountとmessage)の値を変更しており、ステートが変更されるとコンポーネント(今回はApp.js)が再レンダリングされる
换句话说,导致无限循环的机制如下所示。
-
- 画面レンダリング(1回目)
React君『関数「func1(1)」を実行するで〜』
関数「func1(1)」実行(1回目)
React君『countとmessageのステートを変更するで〜』
ステート変更(1回目)
React君「ステートが変わった!コンポーネントを再レンダリングするで〜」
画面レンダリング(2回目)
React君『関数「func1(1)」を実行するで〜』
関数「func1(1)」実行(2回目)
React君『countとmessageのステートを変更するで〜』
…
…
React君「これ…無限ループしてるで!!」
那要怎么办呢?(前面的谈话)
让我们在给出正确答案之前,回顾一下最初的示例调用方式,以及导致无限循环的错误调用方式,并进行比较。
{/* 無限ループしない(サンプル初期の呼び出し) */}
<button onClick={func1}>ボタン1</button>
{/* 無限ループする(NGパターンの呼び出し) */}
<button onClick={func1(1)}>ボタン1</button>
这两者的区别只是有无括号而已。
每个的返回值会怎么样呢?让我来解释一下,以便更容易理解。
{/* 無限ループしない(サンプル初期の呼び出し) */}
<button onClick={() => { return func1(); }}>ボタン1</button>
{/* 無限ループする(NGパターンの呼び出し) */}
<button onClick={ setCount(count + 1); setMessage(`ボタン1がクリックされました!`); }>ボタン1</button>
以“函数名”的形式编写,通过这种方式返回函数作为返回值。
与此相反,后者使用“函数名()”的形式来返回函数的执行结果。
由于只返回执行结果(没有返回值),即使无限循环没有错误并且按钮可以按下,也不会发生计数增加等情况。
React小伙伴:“哦!按钮被点击了!!我会处理onClick的操作…嗯,你只给我执行结果,不给我函数,我无法进行处理!”
那么我们应该怎么做呢?
如果你来到这里,应该就明白了。
在事件的prop中,我们要传递的是函数而不是函数的执行结果。
如果要直接修正NG模式的「onClick={func1(1)}」,那么修改如下。
<button onClick={() => { return func1(1); }}>ボタン1</button>
根据React官方文档,我们采用以下省略了大括号、return和分号的表达方式。这样变得更简单了。
<button onClick={() => func1(1)}>ボタン1</button>
所以,最终完成的源代码完整如下。
import { useState } from "react";
export const App = () => {
const [count, setCount] = useState(0);
const [message, setMessage] = useState("Hello");
const func1 = (n) => {
setCount(count + 1);
setMessage(`ボタン${n}がクリックされました!`);
};
return (
<>
<h1>{count}</h1>
<p>{message}</p>
<button onClick={() => func1(1)}>ボタン1</button>
<button onClick={() => func1(2)}>ボタン2</button>
<button onClick={() => func1(3)}>ボタン3</button>
</>
);
};
如果在点击按钮时,“Hello”部分得到更新,那么就算成功了。太好了,太好了。
请提供更多的上下文。 “参考” 在不同的语境中有不同的意义。
总结
-
- イベント(onClick、onChangeなど)のpropに「関数名()」の形式で書いてしまうと最悪の場合、無限ループしてアプリケーションが動かなくなってしまう
- イベントのpropには関数の実行結果ではなく、関数を渡そう