当我在互联网上搜索react-native
优化/最佳实践(特别是FlatLists
,这往往是贪婪的),我总是发现建议不要使用箭头函数<Component onPress={() => ... }
。
示例1:https://reactnative.dev/docs/optimizing-flatlist-configuration#avoid-anonymous-function-on-renderitem:
将renderItem函数移到渲染函数的外部,这样它就不会在每次渲染函数调用时重新创建自己。(…)
例2:https://blog.codemagic.io/improve-react-native-app-performance/:
避免箭头函数:箭头函数是浪费重新渲染的常见罪魁祸首。不要在渲染视图的函数中使用箭头函数作为回调函数(…)
例3:https://medium.com/wix-engineering/dealing-with-performance-issues-in-react-native-b181d0012cfa:
箭头函数是另一个经常被怀疑浪费的重新渲染。不要在渲染函数中使用箭头函数作为回调函数(如click/tap)
我明白,建议不要使用箭头功能(特别是在onPress
按钮和FlatList
),如果可能的话,把组件放在渲染之外。
良好实践示例:
const IndexScreen = () => {
const onPress = () => console.log('PRESS, change state, etc...')
return (
<>
<Button
onPress={onPress}
/>
<FlatList
...
renderItem={renderItem}
ListFooterComponent={renderFooter}
/>
</>
)
}
const renderItem = ({ item: data }) => <Item data={data} ... />
const renderFooter = () => <Footer ... />
export default IndexScreen
但是,通常,我有其他属性集成到我的子组件中。因此,箭头函数是强制性的:
const IndexScreen = () => {
const otherData = ...(usually it comes from a useContext())...
<FlatList
...
renderItem={({ item: data }) => renderItem(data, otherData)}
/>
}
const renderItem = (data, otherData) => <Item data={data} otherData={otherData} />
export default IndexScreen
在后一种情况下,尽管存在箭头函数,但是否遵循了良好的实践?总之,如果我删除otherData
(为了简单起见),这两种情况是否完全相同,是否遵循了良好的实践?
情形1:
const IndexScreen = () => {
return (
<FlatList
...
renderItem={renderItem}
/>
)
}
const renderItem = ({ item: data }) => <Item data={data} ... />
export default IndexScreen
===情况2 ?
const IndexScreen = () => {
return (
<FlatList
...
renderItem={({ item: data }) => renderItem(data)}
/>
)
}
const renderItem = (data) => <Item data={data} ... />
export default IndexScreen
答案与箭头函数无关,而是理解引用相等性,为什么react可能决定呈现组件。
你可以使用useCallback来包装你的函数。这将导致对renderItem的引用只在你的一个回调依赖项更新时更新。
const renderItem = useCallback(()=>{
...
},
[otherdata]);
第一种情况是理想的,因为当您的应用程序代码运行时,它将只创建一个renderItem
函数。在第二种情况下,即使它没有otherProps,您也没有遵循良好的实践,因为在
renderItem={({ item: data }) => renderItem(data)}
因此,使FlatList每次都呈现。
要解决这个问题,您需要使用useCallback
renderItem
prop中传递的函数。const renderItem = useCallback(({ item: data }) => {
return (<Item data={data} />)
}, []);
...
<FlatList
...
renderItem={renderItem}
/>
,因此记忆版本将只在组件挂载时创建一次。而且,如果您需要在呈现函数中注入更多数据,您可以将该数据定义为useCallback
钩子的依赖项,以便仅在数据更改时创建该函数,从而减少树的呈现。
const renderItem = useCallback(({ item: data }) => {
return (<Item data={data} otherData={otherData} />)
}, [otherData]);
对我来说,正如前面在其他答案中指出的那样,问题主要是由于如果在代码中使用箭头函数,则每次都要重新定义函数。此外,这种定义函数的方式使该函数未命名,因此在调试时更难跟踪:在错误堆栈跟踪中,您可以直接在代码中看到命名函数的名称。
const renderItem = useCallback( function renderItemFunction ({ item: data }) {
return (<Item data={data} otherData={otherData} />)
}, [otherData]);
这样,在错误的堆栈跟踪中,您应该看到renderItemFunction
指示