前言
这是一篇介绍React框架中给JSX对象绑定操作事件的文章,在阅读之前你必须了解react的组件相关知识。
JSX中响应事件
UI界面开发不单单是样式的开发,还涉及一些用户交互事件的接收和处理。在React中是可以给JSX对象添加事件处理函数的。事件处理函数可以是自定义函数,这些处理函数将在浏览器响应用户的交互(如点击、悬停、表单输入框获得焦点等)时触发。
传统的JQuery等JS框架必须编写命令式代码来选择DOM元素,然后将事件处理函数附加到它们上面。React组件中事件处理是声明式的,这些处理事件的函数是UI组件结构的一部。对于咱们前端程序员来说,不必再去追踪分配事件的程序代码,不必去理清事件和元素对应关系,也算是劳动上的减负。
React中给组件元素绑定点击事件并声明事件处理程序的代码如下:
function MyButton({children}) {
function handleClick() {
console.log("你点击了我!");
}
return (
<button onClick={handleClick}>
{children}
</button>
);
}
上述代码中,现在一个组件中定义了一个handleClick的函数,然后通过onClick的props属性传递给了<button>子组件。
所以绑定响应事件的本质上就是,将一个处理函数通过元素属性(props)的方式传递给了子组件元素。当然,这个过程中,对于原生的JSX对象属性名是有点讲究的。内置组件(<button>和<div>)仅支持浏览器事件名称,例如onClick。但是,当咱们构建自己的组件时,则可以按个人喜好命名事件处理函数的prop。
当然在同一个JSX组件元素上可以同时绑定多个事件处理程序用于响应不同的事件:
function MyInput() {
const handleChange = () => {
console.log('changed');
};
const handleBlur = () => {
console.log('blured');
};
return <input onChange={handleChange} onBlur={handleBlur} />;
}
这里有必要强调一下,绑定事件传递给对应props属性的是函数,而不是函数返回值。
// 传递一个函数,点击事件触发的时候运行函数(正确)
<button onClick={handleClick}>按钮</button>
// 调用一个函数,渲染的时候运行函数,然后传递函数返回值(错误)
<button onClick={handleClick()}>按钮</button>
但是如果咱们的目标是:事件触发时将当前的一个静态的值传递给另一个接收参数的函数,那么这种写法就有了些局限。
解决这个问题也不是很麻烦,咱们只要使用内联函数给它包一层,包成标准的不接受参数的函数就行。
function MyButton({children}) {
return (
<button onClick={() => {console.log("clicked");}}>
{children}
</button>
);
}
或者
function MyButton({children}) {
return (
<button onClick={function(){console.log("clicked");}}>
{children}
</button>
);
}
以上所有方式都是等效的。当函数体较短时,内联事件处理函数也会很方便。
自定义事件处理函数
前面提到,对于咱们自己定义的组件时,则可以按个人喜好命名事件处理函数的prop。
function Button({ onAction, children }) {
return (
<button onClick={onAction}>
{children}
</button>
);
}
function PlayButton({ movieName }) {
function handlePlayClick() {
alert(`正在播放 ${movieName}!`);
}
return (
<Button onAction={handlePlayClick}>
播放 "{movieName}"
</Button>
);
}
function UploadButton() {
return (
<Button onAction={() => alert('正在上传!')}>
上传图片
</Button>
);
}
export default function Toolbar() {
return (
<div>
<PlayButton movieName="电影名" />
<UploadButton />
</div>
);
}
上述代码中Button组件就定义了一个onAction的prop用于接收父组件传递过来的处理函数。
子组件向父组件传值
前面介绍的props传递参数,基本上都是父组件传递给子组件的,如果子组件想传递信息给父组件呢?其实就可以通过这种传递回调函数作为props属性参数的方式,将父组件的处理函数传递给子组件,然后由子组件将自己的信息传递给父组件。
function MyButton({index, children, onAction}) {
return (
<button onClick={function(){onAction(index);}}>
{children}
</button>
);
}
function ButtonGroup() {
const names = ['按钮1', '按钮2', '按钮3', '按钮4', '按钮5'];
function handleClick(key) {
console.log('这是第'+key+'个按钮');
}
const listItems = names.map((name, index) =>
<MyButton key={index} index={index} onAction={handleClick}>{name}</MyButton>
);
return (
<div>
{listItems}
</div>
);
}
这样当按钮被点击的时候,作为父组件的ButtonGroup就可以知道是第几个按钮被点击了。
父组件直接分配不同任务给子组件
咱们也可以在父组件中定义子组件的事件处理函数,然后在不同的子组件响应事件下进行不同的处理函数运行。为此,咱们将组件从父组件接收的prop属性参数作为事件处理函数。
function Button({ onClick, children }) {
return (
<button onClick={function(){onClick(children)}}>
{children}
</button>
);
}
export default function Toolbar() {
function playMovie(movieName) {
console.log(`正在播放 ${movieName}!`);
}
function uploadFile() {
console.log('正在上传');
}
return (
<div>
<Button onClick={playMovie} >藏龙卧虎</Button>
<Button onClick={uploadFile} >上传图片</Button>
</div>
);
}
这所以推荐大家这么做,是因为这样设计可以提高我们的代码复用率。比如,播放功能可以扩展,不仅仅是通过按钮点击,也可以通过键盘上的快捷键。上传文件功能不单单的点击按钮触发,也可以是拖拽上传。
合成事件对象使用
前面在说到“内联函数”时,我说将需要参数的函数使用内联函数包一层,包成标准的不接受参数的函数就可以作为触发事件处理函数使用。但是,事件处理函数并不是没有参数的形式,只不过在前面的代码中,我们将那个唯一的参数给忽略了。
当我们使用原生的addEventListener()函数将事件处理函数附加到DOM元素时,回调函数会接收到一个事件参数。React中的事件处理函数也会接收一个事件参数,只不过它不是标准的事件示例,而是被称为SyntheticEvent(直译过来就叫合成事件),是原生事件实例的一个简单包装器。
感兴趣的朋友可以写出类似的代码打印出来看看,其实这个对象中包裹的内容与原生的事件参数类似,有点击的坐标什么的,我也看不太懂。React之所以要包这一层主要的原因就是不同浏览器的事件参数不太一样,包了一层给做了个格式和信息的统一。
function MyButton({children}) {
return (
<button onClick={(e) => {console.log(e);}}>
{children}
</button>
);
}
合成事件对象除了提供了那些用于查看的信息,还有两个比较有的函数: e.stopPropagation()和e.preventDefault()。下面咱们分别来介绍这两个函数的用途。
e.stopPropagation()函数
事件处理函数处理捕获组件自身绑定的事件外,还能捕获任何来自它子组件的事件。从事件发生的地方开始,然后沿着组件树向上传播。
例如:
export default function Toolbar() {
return (
<div className="Toolbar" onClick={() => {
alert('你点击了 toolbar !');
}}>
<button onClick={() => alert('正在播放!')}>
播放电影
</button>
<button onClick={() => alert('正在上传!')}>
上传图片
</button>
</div>
);
}
在上面这个组件中,如果你点击任一按钮,它自身的onClick将首先执行,然后父级<div>的onClick会接着执行。因此会出现两条消息弹窗。这样的过程被称为事件传播,在React中所有的事件都会传播,除了onScroll,它仅适用于你附加到的JSX标签。
要避免“事件传播”的发生,就需要使用e.stopPropagation()函数。当然,这里需要说清楚事件传播并不是Bug而是逻辑,避免事件传播是特例。如果咱们要阻止一个事件传播到其父组件,只需要在绑定事件中调用一个e.stopPropagation()函数即可,如下:
export default function Toolbar() {
return (
<div className="Toolbar" onClick={() => {
alert('你点击了 toolbar !');
}}>
<button onClick={(e) => {
alert('正在播放!');
e.stopPropagation();
}}>
播放电影
</button>
<button onClick={(e) => {
alert('正在上传!');
e.stopPropagation();
}}>
上传图片
</button>
</div>
);
}
这样咱们再次点击按钮时,将只显示一个来自<button>的alert弹窗,而不会再次运行父组件上绑定的点击事件,即阻止第二次弹窗。
e.stopPropagation()函数用于阻止触发绑定在外层标签上的事件处理函数。
e.preventDefault()函数
某些浏览器事件具有与事件相关联的默认行为。例如,点击<form>表单内部的按钮会触发表单提交事件,默认情况下将重新加载整个页面:
export default function Signup() {
return (
<form onSubmit={() => alert('提交表单!')}>
<input />
<button>发送</button>
</form>
);
}
当我们想要阻止这种默认行为时,可以调用合成事件对象中的e.preventDefault()函数。
export default function Signup() {
return (
<form onSubmit={e => {
e.preventDefault();
alert('提交表单!');
}}>
<input />
<button>发送</button>
</form>
);
}
所以当有些时候遇到一些需要规避的浏览器默认行为时,可以调用e.preventDefault()函数来实现需求。
1742

被折叠的 条评论
为什么被折叠?



