如何提取JSX内联函数并用useCallback包裹避免重复创建函数
JSX内联点击函数的useCallback改造方案
你要避免列表渲染时生成重复函数实例,核心是要保证绑定到onClick上的函数引用是稳定的,不要在map循环里创建新的箭头函数。推荐用DOM自定义属性传参的方式实现,代码改动最小:
实现代码
首先在组件的hooks声明区域(和useState/useEffect同层级),定义被useCallback包裹的稳定处理函数:
const handleTagSelect = useCallback((e) => { // 从绑定的DOM节点上读取预存的tag值,注意用currentTarget避免子元素点击时取值错误 const targetTag = e.currentTarget.dataset.tagName; selectTag(targetTag); }, [selectTag]);
之后修改JSX结构,把内联onClick替换成上面的稳定引用,同时给每个标签节点加data属性存对应的tag值:
{suggestedTags.length ? ( <div className={classes.tagSuggestionWrapper}> {suggestedTags.map((tag) => ( <div key={tag} data-tag-name={tag} onClick={handleTagSelect} > {tag} </div> ))} </div> ) : null}
避坑提醒
- 千万不要写成
onClick={() => handleTagSelect(tag)}:这种写法本质还是在每次渲染时给每个列表项创建新的箭头函数实例,和原有代码的表现完全一致,做不到你要的避免重复生成函数的效果。 - 依赖数组必须包含
selectTag:如果selectTag是当前组件内部定义的函数,建议你也给selectTag本身套一层useCallback保证引用稳定,否则外层的handleTagSelect还是会跟着每次渲染重新生成,优化就失效了。 - 取值时用
e.currentTarget不要用e.target:如果后续你给标签项加了内部子元素(比如图标、删除按钮),点击子元素时e.target会指向子元素本身,拿不到存在父节点上的tag值,e.currentTarget会始终指向绑定了onClick事件的div节点,取值更稳定。 - 如果你不想用data属性传参,也可以额外抽离一个独立的TagItem子组件,把tag、selectTag作为props传入,在子组件内部用useCallback包裹点击逻辑,这种写法更符合React组件拆分的规范,但代码改动量会比上面的方案大不少。
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

