响应事件
React 允许您向 JSX 添加事件处理程序。 事件处理程序是您自己的函数,将被触发以响应单击、悬停、聚焦表单输入等交互。
你将学习
- 编写事件处理程序的不同方法
- 如何从父组件传递事件处理逻辑
- 事件如何传播以及如何阻止它们
添加事件处理程序
要添加事件处理程序,您将首先定义一个函数,然后将其作为 prop 传递给适当的 JSX 标记。 例如,这是一个尚未执行任何操作的按钮:
export default function Button() {
return (
<button>
I don't do anything
</button>
);
}
您可以按照以下三个步骤使其在用户单击时显示消息:
- 在 Button 组件内声明一个名为 handleClick 的函数。
- 在该函数内实现逻辑(使用警报显示消息)。
- 将 onClick={handleClick} 添加到JSX的<button>。
export default function Button() {
function handleClick() {
alert('You clicked me!');
}
return (
<button onClick={handleClick}>
Click me
</button>
);
}
您定义了 handleClick 函数,然后将其作为 prop 传递给 <button>。 handleClick 是一个事件处理程序。 事件处理函数:
通常在您的组件内部定义。
名称以 handle 开头,后跟事件的名称。
按照惯例,通常将事件处理程序命名为句柄后跟事件名称。 你会经常看到 onClick={handleClick}、onMouseEnter={handleMouseEnter} 等等。
或者,您可以在 JSX 中内联定义事件处理程序:
<button onClick={function handleClick() {
alert('You clicked me!');
}}>
或者,更简洁地使用箭头函数:
<button onClick={() => {
alert('You clicked me!');
}}>
所有这些风格都是等价的。 内联事件处理程序对于短函数很方便。
"陷阱
传递给事件处理程序的函数必须被传递,而不是被调用。 例如:
传函数(正确) 调用函数(错误) <button onClick={handleClick}> <button onClick={handleClick()}> 区别很微妙。 在第一个示例中,handleClick 函数作为 onClick 事件处理程序传递。 这告诉 React 记住它并且只在用户单击按钮时调用你的函数。
在第二个示例中,handleClick() 末尾的 () 在渲染过程中立即触发该函数,没有任何点击。 这是因为 JSX { 和 } 中的 JavaScript 会立即执行。
当您编写内联代码时,同样的陷阱会以不同的方式出现:
传函数(正确) 调用函数(错误) <button onClick={() => alert('...')}> <button onClick={alert('...')}> 像这样传递内联代码不会在点击时触发——它会在每次组件渲染时触发:
// 此警报在组件呈现时触发,而不是在单击时触发! <button onClick={alert('You clicked me!')}>
如果您想内联定义事件处理程序,请将其包装在匿名函数中,如下所示:
<button onClick={() => alert('You clicked me!')}>
这不是在每次渲染时都执行内部代码,而是创建一个稍后调用的函数。
在这两种情况下,您要传递的是一个函数:
<button onClick={handleClick}> 传递 handleClick 函数。
<button onClick={() => alert('...')}> 传递 () => alert('...') 函数。
阅读有关箭头功能的更多信息。
在事件处理程序中读取props
因为事件处理程序是在组件内部声明的,所以它们可以访问组件的 props。 这是一个按钮,单击该按钮时,会显示带有消息属性的警报:
function AlertButton({ message, children }) {
return (
<button onClick={() => alert(message)}>
{children}
</button>
);
}
export default function Toolbar() {
return (
<div>
<AlertButton message="Playing!">
Play Movie
</AlertButton>
<AlertButton message="Uploading!">
Upload Image
</AlertButton>
</div>
);
}
这让这两个按钮显示不同的消息。 尝试更改传递给他们的消息。
事件处理程序作为props传递
通常您会希望父组件指定子组件的事件处理程序。 考虑按钮:根据您使用按钮组件的位置,您可能想要执行不同的功能——也许一个播放电影而另一个上传图像。
为此,传递组件从其父级接收的prop作为事件处理程序,如下所示:
function Button({ onClick, children }) {
return (
<button onClick={onClick}>
{children}
</button>
);
}
function PlayButton({ movieName }) {
function handlePlayClick() {
alert(`Playing ${movieName}!`);
}
return (
<Button onClick={handlePlayClick}>
Play "{movieName}"
</Button>
);
}
function UploadButton() {
return (
<Button onClick={() => alert('Uploading!')}>
Upload Image
</Button>
);
}
export default function Toolbar() {
return (
<div>
<PlayButton movieName="Kiki's Delivery Service" />
<UploadButton />
</div>
);
}
在这里,Toolbar 组件呈现一个 PlayButton 和一个 UploadButton:
- PlayButton 将 handlePlayClick 作为 onClick 属性传递给内部的 Button。
- UploadButton 将 () => alert('Uploading!') 作为 onClick 属性传递给内部的 Button。
最后,您的 Button 组件接受一个名为 onClick 的prop。 它使用 onClick={onClick} 将该道具直接传递给内置浏览器 <button>。 这告诉 React 在点击时调用传递的函数。
如果您使用设计系统,按钮等组件通常包含样式但不指定行为。 相反,PlayButton 和 UploadButton 等组件将向下传递事件处理程序。
props事件处理程序命名
<button> 和 <div> 等内置组件仅支持浏览器事件名称,如 onClick。 但是,当您构建自己的组件时,您可以按照自己喜欢的方式命名它们的事件处理程序 props。
按照惯例,事件处理程序 props 应该以 on 开头,后跟一个大写字母。
例如,Button 组件的 onClick 属性可以称为 onSmash:
function Button({ onSmash, children }) {
return (
<button onClick={onSmash}>
{children}
</button>
);
}
export default function App() {
return (
<div>
<Button onSmash={() => alert('Playing!')}>
Play Movie
</Button>
<Button onSmash={() => alert('Uploading!')}>
Upload Image
</Button>
</div>
);
}
在此示例中,<button onClick={onSmash}> 显示浏览器 <button>(小写)仍需要一个名为 onClick 的props,但您的自定义 Button 组件接收的props名称由您决定!
当您的组件支持多种交互时,您可以为特定于应用程序的概念命名事件处理程序props。 例如,此 Toolbar 组件接收 onPlayMovie 和 onUploadImage 事件处理程序:
export default function App() {
return (
<Toolbar
onPlayMovie={() => alert('Playing!')}
onUploadImage={() => alert('Uploading!')}
/>
);
}
function Toolbar({ onPlayMovie, onUploadImage }) {
return (
<div>
<Button onClick={onPlayMovie}>
Play Movie
</Button>
<Button onClick={onUploadImage}>
Upload Image
</Button>
</div>
);
}
function Button({ onClick, children }) {
return (
<button onClick={onClick}>
{children}
</button>
);
}
请注意 App 组件如何隐藏 Toolbar 将如何处理 onPlayMovie 或 onUploadImage。 这是工具栏的一个实现细节。 在这里,Toolbar 将它们作为 onClick 处理程序传递给它的 Button,但它稍后也可以在键盘快捷键上触发它们。 在像 onPlayMovie 这样的特定于应用程序的交互之后命名props可以让您灵活地更改它们以后的使用方式。
事件传播
事件处理程序还将捕获来自您的组件可能具有的任何子组件的事件。 我们说一个事件在树上“冒泡”或“传播”:它从事件发生的地方开始,然后在树上向上传播。
这个 <div> 包含两个按钮。 <div> 和每个按钮都有自己的 onClick 处理程序。 您认为单击按钮时会触发哪些处理程序?
export default function Toolbar() {
return (
<div className="Toolbar" onClick={() => {
alert('You clicked on the toolbar!');
}}>
<button onClick={() => alert('Playing!')}>
Play Movie
</button>
<button onClick={() => alert('Uploading!')}>
Upload Image
</button>
</div>
);
}
如果你点击任一按钮,它的 onClick 将首先运行,然后是父 <div> 的 onClick。 所以会出现两条消息。 如果你点击工具栏本身,只有父 <div> 的 onClick 会运行。
"陷阱
除了 onScroll 之外,所有事件都在 React 中传播,它仅适用于您附加到的 JSX 标签。
阻止事件传播
事件处理程序接收一个事件对象作为它们唯一的参数。 按照惯例,它通常被称为 e,代表“事件”。 您可以使用此对象来读取有关事件的信息。
该事件对象还允许您停止传播。 如果你想阻止事件到达父组件,你需要像这个 Button 组件那样调用 e.stopPropagation() :
function Button({ onClick, children }) {
return (
<button onClick={e => {
e.stopPropagation();
onClick();
}}>
{children}
</button>
);
}
export default function Toolbar() {
return (
<div className="Toolbar" onClick={() => {
alert('You clicked on the toolbar!');
}}>
<Button onClick={() => alert('Playing!')}>
Play Movie
</Button>
<Button onClick={() => alert('Uploading!')}>
Upload Image
</Button>
</div>
);
}
当你点击一个按钮时:
- React 调用传递给 <button> 的 onClick 处理程序。
- 该处理程序在 Button 中定义,它执行以下操作:
- 调用 e.stopPropagation(),防止事件进一步冒泡。
- 调用 onClick 函数,它是从 Toolbar 组件传递的一个 prop。
- 该函数在工具栏组件中定义,显示按钮自己的警报。
- 由于传播停止,父 <div> 的 onClick 处理程序不会运行。
作为 e.stopPropagation() 的结果,点击按钮现在只显示一个警报(来自 <button>)而不是两个警报(来自 <button> 和父工具栏 <div>)。 单击按钮与单击周围的工具栏不同,因此停止传播对于此 UI 来说是有意义的。
深度阅读:捕获相位事件
在极少数情况下,您可能需要捕获子元素上的所有事件,即使它们停止了传播。 例如,您可能想要记录每次点击以进行分析,而不考虑传播逻辑。 您可以通过在事件名称末尾添加 Capture 来执行此操作:
<div onClickCapture={() => { /* this runs first */ }}> <button onClick={e => e.stopPropagation()} /> <button onClick={e => e.stopPropagation()} /> </div>
每个事件都分三个阶段传播:
1.它向下移动,调用所有 onClickCapture 处理程序。
2.它运行被点击元素的 onClick 处理程序。
3.它向上移动,调用所有 onClick 处理程序。捕获事件对于路由或分析等代码很有用,但您可能不会在应用程序代码中使用它们。
传递处理程序作为传播的替代方法
请注意此点击处理程序如何运行一行代码,然后调用父级传递的 onClick 属性:
function Button({ onClick, children }) {
return (
<button onClick={e => {
e.stopPropagation();
onClick();
}}>
{children}
</button>
);
}
您也可以在调用父 onClick 事件处理程序之前向此处理程序添加更多代码。 此模式提供了传播的替代方案。 它让子组件处理事件,同时也让父组件指定一些额外的行为。 与传播不同,它不是自动的。 但是这种模式的好处是您可以清楚地跟踪作为某个事件的结果执行的整个链代码。
如果您依赖于传播并且很难跟踪执行了哪些处理程序以及执行原因,请尝试使用此方法。
阻止默认行为
某些浏览器事件具有与之关联的默认行为。 例如,一个 <form> 提交事件,当它里面的一个按钮被点击时发生,默认情况下会重新加载整个页面:
export default function Signup() {
return (
<form onSubmit={() => alert('Submitting!')}>
<input />
<button>Send</button>
</form>
);
}
您可以在事件对象上调用 e.preventDefault() 来阻止这种情况的发生:
export default function Signup() {
return (
<form onSubmit={e => {
e.preventDefault();
alert('Submitting!');
}}>
<input />
<button>Send</button>
</form>
);
}
不要混淆 e.stopPropagation() 和 e.preventDefault()。 它们都很有用,但不相关:
- e.stopPropagation() 停止触发附加到上述标签的事件处理程序。
- e.preventDefault() 阻止少数事件的默认浏览器行为。
事件处理程序可以有副作用吗?
绝对地! 事件处理程序是处理副作用的最佳场所。
与渲染函数不同,事件处理程序不需要是纯粹的,因此它是更改某些内容的好地方——例如,更改输入的值以响应键入,或更改列表以响应按钮按下。 然而,为了改变一些信息,你首先需要一些方法来存储它。 在 React 中,这是通过使用状态(组件的内存)来完成的。 您将在下一页了解所有相关信息。
回顾
- 您可以通过将函数作为 prop 传递给 <button> 等元素来处理事件。
- 必须传递事件处理程序,而不是调用它! onClick={handleClick},而不是 onClick={handleClick()}。
- 您可以单独或内联定义事件处理函数。
- 事件处理程序在组件内部定义,因此它们可以访问props。
- 您可以在父级中声明一个事件处理程序并将其作为props传递给子级。
- 您可以使用特定于应用程序的名称定义自己的事件处理程序props。
- 事件向上传播。 在第一个参数上调用 e.stopPropagation() 以防止这种情况发生。
- 事件可能有不需要的默认浏览器行为。 调用 e.preventDefault() 来防止这种情况。
- 从子处理程序显式调用事件处理程序 prop 是传播的一个很好的替代方法。
网友评论