React lets you add event handlers to your JSX. Event handlers are your own functions that will be triggered in response to interactions like clicking, hovering, focusing form inputs, and so on.
To learn
- Different ways to write an event handler
- How to pass event handling logic from a parent component(如何从父组件传递事件处理逻辑)
- How events propagate and how to stop them(事件如何传播以及如何停止它们)
Adding event handlers
To add an event handler, you will first define a function and then pass it as a prop to the appropriate JSX tag(如需添加一个事件处理函数,你需要先定义一个函数,然后 将其作为 prop 传入 合适的 JSX 标签。). For example, here is a button that doesn’t do anything yet:
1 2 3 4 5 6 7 |
export default function Button() { return ( <button> I don't do anything </button> ); } |
You can make it show a message when a user clicks by following these three steps(句子意思是”You can make it show a message by following these three steps when a user clicks “,别翻译错了):
- Declare a function called
handleClick
inside yourButton
component.(注意是在你的组件中定义事件处理方法) - Implement the logic inside that function (use
alert
to show the message). - Add
onClick={handleClick}
to the<button>
JSX.
1 2 3 4 5 6 7 8 9 10 11 12 |
export default function Button() { /* ██ 这个事件处理函数会传递给 <button ../> 按钮 ██ */ function handleClick() { alert('你点击了我!'); } return ( <button onClick={handleClick}> 点我 </button> ); } |
You defined the handleClick
function and then passed it as a prop to <button>
. handleClick
is an event handler. Event handler functions:
- Are usually defined inside your components.(事件处理函数通常写在你的组件内部)
- Have names that start with
handle
, followed by the name of the event.(事件处理函数的命名,通常以 handle 开头,然后跟上具体事件的名字)
By convention(按照惯例), it is common to name event handlers as handle
followed by the event name. You’ll often see onClick={handleClick}
, onMouseEnter={handleMouseEnter}
, and so on.
Alternatively(或者、做为选择), you can define an event handler inline in the JSX:
1 2 3 |
<button onClick={function handleClick() { alert('你点击了我!'); }}> |
Or, more concisely, using an arrow function:
1 2 3 |
<button onClick={() => { alert('You clicked me!'); }}> |
All of these styles are equivalent. Inline event handlers are convenient for short functions.
Pitfall(陷阱):
Functions passed to event handlers must be passed, not called(事件处理函数应该只传递,而非调用). For example:
passing a function (correct) calling a function (incorrect) <button onClick={handleClick}>
<button onClick={handleClick()}>
The difference is subtle(区别很微妙). In the first example, the
handleClick
function is passed as anonClick
event handler. This tells React to remember it and only call your function when the user clicks the button.In the second example, the
()
at the end ofhandleClick()
fires the function immediately during rendering, without any clicks. This is because JavaScript inside the JSX{
and}
executes right away.【在第二个示例中,handleClick()
中最后的()
会在每次 渲染 过程中 立即 触发函数,即使没有任何点击。这是因为在 JSX{
和}
之间的 JavaScript 会立即执行。】When you write code inline, the same pitfall presents itself in a different way:
passing a function (correct) calling a function (incorrect) <button onClick={() => alert('...')}>
<button onClick={alert('...')}>
因为 () => alert('...')
只是定义了一个函数,这就是定义函数的语法(在Java中亦是如此)!Passing inline code like this won’t fire on click—it fires every time the component renders:
12 // This alert fires when the component renders, not when clicked!<button onClick={alert('You clicked me!')}>If you want to define your event handler inline, wrap it in an anonymous function like so:
1 <button onClick={() => alert('You clicked me!')}>Rather than executing the code inside with every render, this creates a function to be called later.
In both cases, what you want to pass is a function:
<button onClick={handleClick}>
passes thehandleClick
function.【<button onClick={handleClick}>
传递了handleClick
函数。】<button onClick={() => alert('...')}>
passes the() => alert('...')
function. 【<button onClick={() => alert('...')}>
传递了() => alert('...')
函数。】
Reading props in event handlers (在事件处理函数中读取 props )
Because event handlers are declared inside of a component, they have access to the component’s props(它们可以直接访问组件的 props). Here is a button that, when clicked, shows an alert with its message
prop:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
function AlertButton({ message, children }) { return ( <button onClick={() => alert(message)}> {children} </button> ); } export default function Toolbar() { return ( <div> <AlertButton message="正在播放!"> 播放电影 </AlertButton> <AlertButton message="正在上传!"> 上传图片 </AlertButton> </div> ); } |
This lets these two buttons show different messages. Try changing the messages passed to them.
Passing event handlers as props (将事件处理函数作为 props 传递)
Often you’ll want the parent component to specify a child’s event handler. Consider buttons: depending on where you’re using a Button
component, you might want to execute a different function—perhaps one plays a movie and another uploads an image.
To do this, pass a prop the component receives from its parent as the event handler like so:
【先是 pass a prop 意思是”传递一个prop”,然后 the component receives from its parent 是定语从句修饰 prop,意思是这个 prop 是这个 component 从它的父组件里接收的。as the event handler 是指 prop as the event handler。==》最终翻译就是:子 component 从父 component 接收了一个 prop,而这个 prop 作为子 component 的事件处理函数】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
// 这里 Button 是子组件,它接收的事件处理函数 onClick 是从父组件 PlayButton 传递过来的 function Button({ onClick, children }) { return ( <button onClick={onClick}> {children} </button> ); } function PlayButton({ movieName }) { function handlePlayClick() { alert(`正在播放 ${movieName}!`); } return ( <Button onClick={handlePlayClick}> 播放 "{movieName}" </Button> ); } function UploadButton() { return ( <Button onClick={() => alert('正在上传!')}> 上传图片 </Button> ); } export default function Toolbar() { return ( <div> <PlayButton movieName="魔女宅急便" /> <UploadButton /> </div> ); } |
Here, the Toolbar
component renders a PlayButton
and an UploadButton
:
PlayButton
passeshandlePlayClick
as theonClick
prop to theButton
inside.UploadButton
passes() => alert('Uploading!')
as theonClick
prop to theButton
inside.
Finally, your Button
component accepts a prop called onClick
. It passes that prop directly to the built-in browser <button>
with onClick={onClick}
. This tells React to call the passed function on click.
If you use a design system, it’s common for components like buttons to contain styling but not specify behavior. Instead, components like PlayButton
and UploadButton
will pass event handlers down.
Naming event handler props (命名事件处理函数 prop)
Built-in components like <button>
and <div>
only support browser event names like onClick
. However, when you’re building your own components, you can name their event handler props any way that you like.
By convention, event handler props should start with on
, followed by a capital letter.(按照惯例,事件处理函数 props 应该以 on
开头,后跟一个大写字母。)
For example, the Button
component’s onClick
prop could have been called onSmash
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
function Button({ onSmash, children }) { return ( <button onClick={onSmash}> {children} </button> ); } export default function App() { return ( <div> <Button onSmash={() => alert('正在播放!')}> 播放电影 </Button> <Button onSmash={() => alert('正在上传!')}> 上传图片 </Button> </div> ); } |
In this example, <button onClick={onSmash}>
shows that the browser <button>
(lowercase) still needs a prop called onClick
, but the prop name received by your custom Button
component is up to you!【翻译:received by your custom Button
component 是定语从句,修饰 the prop name,意思是这个 prop name 被你自定义的 Button 组件接收,这个 prop name 是啥取决于你】
When your component supports multiple interactions, you might name event handler props for app-specific concepts(当您的组件支持多个交互时,您可以为特定于应用的概念,命名 event handler props). For example, this Toolbar
component receives onPlayMovie
and onUploadImage
event handlers:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
export default function App() { return ( <Toolbar onPlayMovie={() => alert('正在播放!')} onUploadImage={() => alert('正在上传!')} /> ); } function Toolbar({ onPlayMovie, onUploadImage }) { return ( <div> <Button onClick={onPlayMovie}> 播放电影 </Button> <Button onClick={onUploadImage}> 上传图片 </Button> </div> ); } function Button({ onClick, children }) { return ( <button onClick={onClick}> {children} </button> ); } |
Notice how the App
component does not need to know what Toolbar
will do with onPlayMovie
or onUploadImage
. That’s an implementation detail of the Toolbar
. Here, Toolbar
passes them down as onClick
handlers to its Button
s, but it could later also trigger them on a keyboard shortcut. Naming props after app-specific interactions like onPlayMovie
gives you the flexibility to change how they’re used later.
Note
Make sure that you use the appropriate HTML tags for your event handlers(确保为事件处理程序使用适当的 HTML 标签). For example, to handle clicks, use <button onClick={handleClick}>
instead of <div onClick={handleClick}>
. Using a real browser <button>
enables built-in browser behaviors like keyboard navigation. If you don’t like the default browser styling of a button and want to make it look more like a link or a different UI element, you can achieve it with CSS. Learn more about writing accessible markup.
Event propagation (██ 事件传播 ██)
Event handlers will also catch events from any children your component might have. We say that an event “bubbles” or “propagates” up the tree: it starts with where the event happened, and then goes up the tree.【事件处理函数还将捕获任何来自子组件的事件。通常,我们会说事件会沿着树向上“冒泡”或“传播”:它从事件发生的地方开始,然后沿着树向上传播。】bubble 冒泡;
This <div>
contains two buttons. Both the <div>
and each button have their own onClick
handlers. Which handlers do you think will fire when you click a button?(下面这个 <div>
包含两个按钮。<div>
和每个按钮都有自己的 onClick
处理函数。你认为点击按钮时会触发哪些处理函数?)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
export default function Toolbar() { return ( <div className="Toolbar" onClick={() => { alert('你点击了 toolbar !'); }}> <button onClick={() => alert('正在播放!')}> 播放电影 </button> <button onClick={() => alert('正在上传!')}> 上传图片 </button> </div> ); } |
If you click on either button, its onClick
will run first, followed(跟着) by the parent <div>
’s onClick
. So two messages will appear. If you click the toolbar itself, only the parent <div>
’s onClick
will run.(如果你点击任一按钮,它自身的 onClick
将首先执行,然后父级 <div>
的 onClick
会接着执行。因此会出现两条消息。如果你点击 toolbar 本身,将只有父级 <div>
的 onClick
会执行。)
Pitfall
All events propagate in React except onScroll
, which only works on the JSX tag you attach it to.(在 React 中所有事件都会传播,除了 onScroll
,它仅适用于你附加到的 JSX 标签。)
Stopping propagation (阻止事件传播)
Event handlers receive an event object as their only argument. By convention, it’s usually called e
, which stands for “event”. You can use this object to read information about the event.
stands for 代表;支持;
That event object also lets you stop the propagation. If you want to prevent an event from reaching parent components, you need to call e.stopPropagation()
like this Button
component does:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
function Button({ onClick, children }) { return ( <button onClick={e => { e.stopPropagation(); onClick(); }}> {children} </button> ); } export default function Toolbar() { return ( <div className="Toolbar" onClick={() => { alert('你点击了 toolbar !'); }}> <Button onClick={() => alert('正在播放!')}> 播放电影 </Button> <Button onClick={() => alert('正在上传!')}> 上传图片 </Button> </div> ); } |
When you click on a button:
- React calls the
onClick
handler passed to<button>
. - That handler, defined in
Button
, does the following:- Calls
e.stopPropagation()
, preventing the event from bubbling further(阻止事件进一步冒泡。). - Calls the
onClick
function, which is a prop passed from theToolbar
component(调用onClick
函数,它是从Toolbar
组件传递过来的 prop。).
- Calls
- That function, defined in the
Toolbar
component, displays the button’s own alert. - Since the propagation was stopped, the parent
<div>
’sonClick
handler does not run.
As a result of e.stopPropagation()
, clicking on the buttons now only shows a single alert (from the <button>
) rather than the two of them (from the <button>
and the parent toolbar <div>
). Clicking a button is not the same thing as clicking the surrounding toolbar, so stopping the propagation makes sense for this UI.
Passing handlers as alternative to propagation (”传递处理函数”作为”事件传播”的替代方案)
Notice how this click handler runs a line of code and then calls the onClick
prop passed by the parent(注意,此处的点击事件处理函数先执行了一行代码,然后调用了父组件传递的 onClick
prop):
1 2 3 4 5 6 7 8 9 10 |
function Button({ onClick, children }) { return ( <button onClick={e => { e.stopPropagation(); onClick(); }}> {children} </button> ); } |
You could add more code to this handler before calling the parent onClick
event handler, too. This pattern provides an alternative to propagation. It lets the child component handle the event, while also letting the parent component specify some additional behavior. Unlike propagation, it’s not automatic. But the benefit of this pattern is that you can clearly follow the whole chain of code that executes as a result of some event.(你也可以在调用父元素 onClick
函数之前,向这个处理函数添加更多代码。此模式是事件传播的另一种 替代方案 。它让子组件处理事件,同时也让父组件指定一些额外的行为。与事件传播不同,它并非自动。但使用这种模式的好处是你可以清楚地追踪因某个事件的触发而执行的整条代码链。)
If you rely on propagation and it’s difficult to trace which handlers execute and why, try this approach instead.
Preventing default behavior (阻止默认行为)
Some browser events have default behavior associated with them(某些浏览器事件具有与事件相关联的默认行为。). For example, a <form>
submit event, which happens when a button inside of it is clicked, will reload the whole page by default:
1 2 3 4 5 6 7 8 |
export default function Signup() { return ( <form onSubmit={() => alert('提交表单!')}> <input /> <button>发送</button> </form> ); } |
You can call e.preventDefault()
on the event object to stop this from happening:
1 2 3 4 5 6 7 8 9 10 11 |
export default function Signup() { return ( <form onSubmit={e => { e.preventDefault(); alert('Submitting!'); }}> <input /> <button>Send</button> </form> ); } |
Don’t confuse e.stopPropagation()
and e.preventDefault()
. They are both useful, but are unrelated:
e.stopPropagation()
stops the event handlers attached to the tags above from firing.e.preventDefault()
prevents the default browser behavior for the few events that have it.
Can event handlers have side effects? (事件处理函数可以包含副作用吗?)
Absolutely! Event handlers are the best place for side effects.
Unlike rendering functions, event handlers don’t need to be pure, so it’s a great place to change something—for example, change an input’s value in response to typing(更改输入框的值以响应键入), or change a list in response to a button press. However, in order to change some information, you first need some way to store it. In React, this is done by using state, a component’s memory. You will learn all about it on the next page.
Recap(摘要)
- You can handle events by passing a function as a prop to an element like
<button>
. - Event handlers must be passed, not called!
onClick={handleClick}
, notonClick={handleClick()}
. - You can define an event handler function separately or inline.
- Event handlers are defined inside a component, so they can access props.
- You can declare an event handler in a parent and pass it as a prop to a child.
- You can define your own event handler props with application-specific names.
- Events propagate upwards. Call
e.stopPropagation()
on the first argument to prevent that. - Events may have unwanted default browser behavior. Call
e.preventDefault()
to prevent that. - Explicitly calling an event handler prop from a child handler is a good alternative to propagation.
- 你可以通过将函数作为 prop 传递给元素如
<button>
来处理事件。 - 必须传递事件处理函数,而非函数调用!
onClick={handleClick}
,不是onClick={handleClick()}
。 - 你可以单独或者内联定义事件处理函数。
- 事件处理函数在组件内部定义,所以它们可以访问 props。
- 你可以在父组件中定义一个事件处理函数,并将其作为 prop 传递给子组件。
- 你可以根据特定于应用程序的名称定义事件处理函数的 prop。
- 事件会向上传播。通过事件的第一个参数调用
e.stopPropagation()
来防止这种情况。 - 事件可能具有不需要的浏览器默认行为。调用
e.preventDefault()
来阻止这种情况。 - 从子组件显式调用事件处理函数 prop 是事件传播的另一种优秀替代方案。