Skip to content
This page has been auto-translated and may contain errors.View in English

事件

你已经构建了一个按钮。现在它需要在用户点击它时真正做一些事情,比如保存表单、打开菜单或删除项目。React 处理事件的方式和处理其他所有东西的方式一样:通过属性。与其像在 JavaScript 事件章节 中那样调用 addEventListener,不如直接把一个函数传给你要监听的元素的属性。核心思想是一样的——一个函数在某事发生时运行——但在 React 中,连接函数的方式是独特的模式。

添加事件处理程序

jsx
function SaveButton() {
  function handleClick() {
    console.log('clicked')
  }
  return <button onClick={handleClick}>Save</button>
}

事件属性采用 camelCase 命名,与它们监听的事件相对应:onClick 用于点击事件,onChange 用于字段值改变,onSubmit 用于表单提交。你在元素上写上这个属性,给它传一个要运行的函数。

handleClick 定义在组件内部,然后传给 onClick。React 在按钮被点击时调用它。你是按名称传递函数 handleClick。如果调用它 handleClick(),会在渲染期间立即运行。你是把函数交给 React 让它稍后运行,而不是现在自己运行它。

当你需要了解发生了什么的细节时,React 会把一个 事件对象 传给你的处理程序,它带有你从 addEventListener 中期望的那些熟悉的属性,比如 targetpreventDefault() 等等。定义一个参数来接收它,通常叫 event

jsx
function SearchBox() {
  function handleChange(event) {
    console.log(event.target.value)
  }
  return <input onChange={handleChange} />
}

event.target 是触发事件的元素,event.target.value 是字段中当前的文本。只有当处理程序真正需要这些信息时,你才需要去使用事件对象。按钮的 onClick 经常不需要。

最常见的错误

因为处理程序是你传给属性的函数,这个错误几乎每个人都会犯过一次:

jsx
// 错误:在组件渲染时立即调用 handleClick
<button onClick={handleClick()}>Save</button>

// 正确:传递函数本身,React 在点击时调用它
<button onClick={handleClick}>Save</button>

括号就是全部区别。handleClick() 在渲染期间立即运行函数,它返回的任何东西(通常是 undefined)就是最终传给 onClick 的值。handleClick 把函数本身交给 React,让它在点击真正发生时调用。 如果一个处理程序似乎在页面加载的那一刻就自动触发了,几乎总是因为这个原因。

将处理程序作为属性传递给子组件

处理程序不一定要和附加它的元素在同一个组件里。你可以在父组件中定义它,然后像传递其他值一样把它作为属性传给子组件:

jsx
function SaveButton({ onSave }) {
  return <button onClick={onSave}>Save</button>
}

function Form() {
  function handleSave() {
    console.log('saved')
  }
  return <SaveButton onSave={handleSave} />
}

Form 拥有 handleSave,把它以 onSave 属性名传给 SaveButtonSaveButton 不知道也不在乎这个函数做什么,它只是把它连接到 onClick。这就是小的、可复用组件上的点击事件如何触发应用程序更高层的逻辑的方式,靠近那些逻辑需要的 state 或数据。

阻止默认行为

有些事件自带浏览器行为,表单提交是你首先会遇到的。默认情况下,提交表单会重新加载页面,这会清除你的组件正在做的任何事情。在处理程序中调用 event.preventDefault() 来阻止这种行为:

jsx
function SignupForm() {
  function handleSubmit(event) {
    event.preventDefault()
    console.log('form submitted')
  }
  return (
    <form onSubmit={handleSubmit}>
      <input type="email" />
      <button type="submit">Sign up</button>
    </form>
  )
}

onSubmit 附加到 <form> 本身,而不是 onClick 到按钮。这样在字段内按 Enter 也会提交表单,不只是点击按钮。

React 交给你的事件对象不是原生浏览器事件。它是 React 在原生事件周围构建的一个包装器,消除浏览器之间的差异,使相同的代码在任何地方都能工作,而你无需检查哪个浏览器触发了事件。它包含你期望的属性,比如 targetpreventDefault()key 等等,但它是 React 自己的对象。

React 也不会给每个有 onClickonChange 属性的元素都附加一个监听器。它在应用的根部附加了一个单独的监听器,并使用事件冒泡来确定给定事件应该运行哪个组件的处理程序。这就是为什么事件处理对那些在应用首次渲染时还不存在的元素也能工作——添加到列表中的项目稍后仍然会正确地触发它的 onClick,因为根部的监听器从一开始就从未与那个特定元素绑定。

Juno处理程序是属性上的函数 事件属性的命名方式像 onClickonChange,你给它们一个函数去运行,而不是运行它的结果。写 onClick={handleClick},永远不要 onClick={handleClick()}。当处理程序需要了解发生了什么的细节时,用一个叫 event 的参数来接收它。
Juno处理程序是属性上的函数 用像 onClickonChangeonSubmit 这样的 camelCase 属性来附加处理程序,传递函数引用,而不是调用结果。在父组件中定义处理程序,当逻辑应该更靠上时把它作为属性传给子组件。在提交处理程序中调用 event.preventDefault(),否则页面会在你的 state 下重新加载。
Juno处理程序是属性上的函数 React 的事件对象是一个合成的包装器,包裹了原生事件,在浏览器之间标准化。React 在根部附加了一个单独的监听器而不是每个元素一个,依靠冒泡把事件路由给正确的处理程序。日常规则仍然适用:把函数交给 React,不要自己调用它,在表单的 onSubmit 上调用 event.preventDefault() 来接管浏览器的默认导航。

接下来:条件渲染,组件返回的内容开始取决于它拥有的数据。