如何在纯函数式 React 组件中处理事件
How to handle events in pure functional React components
根据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
。但是,如果我们想避免在渲染函数中创建函数,那么我们需要将其设为 class 组件。
这是一个非常常见的模式,我想找到一种简单的方法来处理这个问题,使用功能组件,而不会导致性能问题。你有什么建议?
如果没有办法,我将完全无法使用功能组件。因为我经常认为,在某些时候我需要将每个功能组件转换为 class.
经过一番思考,我想我找到了方法。我意识到我们需要更多的 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
.
调度操作
将 onItemClicked
函数作为 ItemView
上的道具传递,并将其用作 onClick
函数。在 onItemClicked
的实现中让它接收一个事件,并从事件中获取 item
。
<ItemView item={item} onItemClicked={onItemClicked}/>
const ItemView = ({item, onItemClicked}) => (
<li><a item={item} onClick={onItemClicked}>{item.name}</a></li>
)
根据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
。但是,如果我们想避免在渲染函数中创建函数,那么我们需要将其设为 class 组件。
这是一个非常常见的模式,我想找到一种简单的方法来处理这个问题,使用功能组件,而不会导致性能问题。你有什么建议?
如果没有办法,我将完全无法使用功能组件。因为我经常认为,在某些时候我需要将每个功能组件转换为 class.
经过一番思考,我想我找到了方法。我意识到我们需要更多的 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
.
将 onItemClicked
函数作为 ItemView
上的道具传递,并将其用作 onClick
函数。在 onItemClicked
的实现中让它接收一个事件,并从事件中获取 item
。
<ItemView item={item} onItemClicked={onItemClicked}/>
const ItemView = ({item, onItemClicked}) => (
<li><a item={item} onClick={onItemClicked}>{item.name}</a></li>
)