React Native中setState与JSX props禁用箭头函数的解决方案困惑
嘿,我太懂你这种纠结了!当初刚在React Native里摸爬滚打的时候,我也对着「JSX props不能用箭头函数/bind」这条规则抓耳挠腮——明明箭头函数写起来那么丝滑,为啥非要绕远路?结合你提到的setState场景,我给你拆解几个项目里亲测好用的解决方案:
这是最传统的解决思路,把需要绑定的方法在组件实例化时就绑定一次,避免每次渲染都创建新的函数引用。
class Counter extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; // 只在组件初始化时执行一次绑定 this.handleIncrement = this.handleIncrement.bind(this); } handleIncrement() { // 这里的this已经正确指向组件实例了 this.setState(prevState => ({ count: prevState.count + 1 })); } render() { // 直接传递绑定好的方法,不会每次渲染生成新函数 return <Button onPress={this.handleIncrement} title="点击加1" />; } }
如果你觉得写constructor里的bind太繁琐,可以用ES7的类属性箭头函数——它会自动把this绑定到组件实例,而且只会在组件实例化时创建一次函数,不会每次渲染重复生成。
class Counter extends React.Component { // 直接初始化state,不用写constructor state = { count: 0 }; // 类属性箭头函数,自动绑定this handleIncrement = () => { this.setState(prevState => ({ count: prevState.count + 1 })); }; render() { return <Button onPress={this.handleIncrement} title="点击加1" />; } }
注意:这个语法需要你的项目支持ES7类属性(通常React Native项目默认已经支持),如果不行的话可以配置Babel插件。
最头疼的就是列表渲染时要给按钮传递item.id这类参数,直接写onPress={() => this.handleDelete(item.id)}又会触发规则。这里给你两个靠谱的解决办法:
玩法一:拆分子组件,让子组件持有参数
把列表项拆成独立的子组件,把参数和回调函数作为props传给子组件。子组件内部的箭头函数只会在自身props变化时重新创建,而父组件的回调函数是缓存好的,不会频繁触发子组件重渲染。
// 子组件:用React.memo包裹,只有props变化时才重渲染 const ListItem = React.memo(({ item, onDelete }) => { // 子组件内部的箭头函数,只在item或onDelete变化时重新生成 const handlePress = () => onDelete(item.id); return <Button onPress={handlePress} title={`删除${item.name}`} />; }); // 父组件 class ItemList extends React.Component { state = { items: [{ id: 1, name: '苹果' }, { id: 2, name: '香蕉' }] }; // 类属性箭头函数,缓存的回调 handleDelete = (itemId) => { this.setState(prevState => ({ items: prevState.items.filter(item => item.id !== itemId) })); }; render() { return ( <View> {this.state.items.map(item => ( <ListItem key={item.id} item={item} onDelete={this.handleDelete} /> ))} </View> ); } }
玩法二:用useCallback(函数组件专属)
如果是函数组件,React提供了useCallback钩子来缓存函数,只有当依赖项变化时才会重新创建函数引用。
import React, { useState, useCallback } from 'react'; const ItemList = () => { const [items, setItems] = useState([{ id: 1, name: '苹果' }, { id: 2, name: '香蕉' }]); // 用useCallback缓存函数,空依赖数组表示只创建一次 const handleDelete = useCallback((itemId) => { setItems(prevItems => prevItems.filter(item => item.id !== itemId)); }, []); return ( <View> {items.map(item => ( <Button key={item.id} onPress={() => handleDelete(item.id)} title={`删除${item.name}`} /> ))} </View> ); };
如果你的回调函数依赖其他变量,记得把变量加到useCallback的依赖数组里,比如依赖items的话就写成useCallback(..., [items])。
其实在大多数普通应用里,箭头函数的性能影响微乎其微——React官方也提到过,除非你有上百个列表项或者明显的性能瓶颈,否则不用过度纠结这条规则。但掌握这些方法能让你的代码更规范,也能在大型项目里避免潜在的性能问题。
内容的提问来源于stack exchange,提问作者quirky purple

