如何在纯函数式 React 组件中处理事件
Posted
技术标签:
【中文标题】如何在纯函数式 React 组件中处理事件【英文标题】:How to handle events in pure functional React components 【发布时间】:2017-04-22 05:10:02 【问题描述】:根据Cam Jackson,我们应该使用Redux,并编写小的、无状态的、函数式的组件。这是一个例子:
const ListView = (items) => (
<ul>
items.map(item => <ItemView item=item/>)
</ul>
)
const ItemView = (item) => (
<li><a>item.name</a></li>
)
现在,假设您要处理鼠标点击,并触发一个动作来获取被点击的项目。我认为这是一个非常好的方法:
const ListView = (items, onItemClicked) =>
const _onClick = item => event =>
event.preventDefault()
onItemClicked(item)
return (
<ul>
items.map(item => <ItemView item=item onClick=_onClick(item)/>)
</ul>
)
const ItemView = (item, onClick) => (
<li><a onClick=onClick>item.name</a></li>
)
但是,根据Esa-Matti Suuronen,这是一种反模式,会导致性能下降。
显然,可以在ItemView
组件内处理事件,并注入item
。但是,如果我们想避免在渲染函数中创建函数,那么我们需要将其设为类组件。
这是一种非常常见的模式,我想找到一种简单的方法来处理这个问题,使用功能组件,而不会导致性能问题。你有什么建议?
如果没有办法做到这一点,我将根本无法使用功能组件。因为我经常认为,在某些时候我需要将每个功能组件转换为一个类。
【问题讨论】:
您可以将_onClick
移到函数之外 - 这样,它仍然在范围内,但不会在每次组件呈现时重新初始化。也就是说,到那时将它变成一个类可能会更干净。
没有。你没有抓住重点。首先,你不能这样做,因为它引用了 onItemClicked
属性。当然你可以传入它。但关键是它每次都会创建一个新的 bound 函数。请注意,_onClick
是一个返回函数的函数(我猜它被称为高阶函数)。由于ItemView
每次都会获得一个新的道具,这将破坏“纯渲染”优化。问题是是否有办法在不使用类的情况下完成这项工作。
抱歉,我不应该在早上喝咖啡之前发表评论!在这种情况下,我不确定解决方案。
【参考方案1】:
在做了更多思考之后,我想我已经找到了方法。我意识到我们需要更多的 Redux 容器。不仅适用于主要的***组件,也适用于可重用的子组件。
上面的例子可以这样解决:
const ListView = (items, onItemClicked) => (
<ul>
items.map(item => <ItemContainer item=item onItemClicked=onItemClicked/>)
</ul>
)
const ItemView = (item, onClick) => (
<li><a onClick=onClick>item.name</a></li>
)
const mapDispatch = (dispatch, item, onItemClicked) => (
onClick: (event) =>
event.preventDefault()
onItemClicked(item)
)
const ItemContainer = connect(null, mapDispatch)(ItemView)
在许多情况下,我们不需要将回调从外部组件传递给内部组件,因为我们将能够直接从ItemContainer
调度操作。
【讨论】:
这不是有同样的问题吗?每次调用 mapDispatch 时,都会向 onClick 属性传入一个新函数。mapDispatch
在每次组件接收到新的 props 时被调用(在这种情况下是 item
,假设 onItemClicked
没有改变)。在这种情况下,我们希望组件重新渲染,因为它代表一个新项目。在原始示例中,all ItemView
将在每次更改任何单个项目时重新呈现。【参考方案2】:
将onItemClicked
函数作为ItemView
上的道具传递,并将其用作onClick
函数。在onItemClicked
的实现中让它接收一个事件,并让item
关闭该事件。
<ItemView item=item onItemClicked=onItemClicked/>
const ItemView = (item, onItemClicked) => (
<li><a item=item onClick=onItemClicked>item.name</a></li>
)
【讨论】:
好的,感谢您的建议,但它实际上并没有回答如何将项目从活动中移除的问题。 const item = event.target.item 这不起作用,您会收到此错误: 标记上的 Unknown propitem
。从元素中移除这个道具 但是,可以将引用作为数据属性存储在 DOM 中,例如data-item=item.id
。这实际上是我们一直在做的事情。它仅支持字符串,因此您只能存储 ID 之类的内容,或将数据序列化为 JSON。我不太喜欢这个解决方案。以上是关于如何在纯函数式 React 组件中处理事件的主要内容,如果未能解决你的问题,请参考以下文章
React面向组件编程 - 基本理解和使用 - 组件三大核心属性state-props-refs - 事件处理 - 非受控组件 - 受控组件 - 高阶函数 - 函数柯里化