如何在纯函数式 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>
)