使用 Hooks 进行 React Native 性能优化
工作还行,但不是那么好?😑。
开发人员很容易就能写出能运行的react-native
应用,但性能却并非最佳的 React Native 应用。一段时间以来,我一直在寻找一种方法来编写性能最佳的 React Native 应用。我尝试了所有可能的最佳实践来让应用变得更好。
以下是我收集的一些提升 React Native 应用性能的方法、技巧和窍门🔥。
1. 停止使用内联函数
首先,现在停止使用内联函数调用,React Native 认为具有内联函数的 props 每次都是一个新值,这会导致其子组件中不必要的重新渲染。
解决方案
将所有内联函数移至 const。(以及下一步)
例子:
前
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
export default ()=>{ | |
const [childState, setChildState] = useState(0); | |
return( | |
<FlatList | |
renderItem={ | |
({item})=>( | |
<AwesomeChild {...item} onPress={(number)=>{setChildState(number)}} /> | |
)} | |
keyExtractor={(item)=>`awesome-child-key-${item.id}`} | |
/>) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
export default ()=>{ | |
const [childState, setChildState] = useState(0); | |
return( | |
<FlatList | |
renderItem={ | |
({item})=>( | |
<AwesomeChild {...item} onPress={(number)=>{setChildState(number)}} /> | |
)} | |
keyExtractor={(item)=>`awesome-child-key-${item.id}`} | |
/>) | |
} |
之后✅
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
export default ()=>{ | |
const [childState, setChildState] = useState(0); | |
//Moved functions | |
const awesomeChildListRenderItem = ({item})=>(<AwesomeChild {...item} onPress={(number)=>{setChildState(number)}} />) | |
const awesomeChildListKeyExtractor = (item)=>(`awesome-child-key-${item.id}`); | |
return( | |
<FlatList | |
renderItem={awesomeChildListRenderItem} | |
keyExtractor={awesomeChildListKeyExtractor} | |
/>) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
export default ()=>{ | |
const [childState, setChildState] = useState(0); | |
//Moved functions | |
const awesomeChildListRenderItem = ({item})=>(<AwesomeChild {...item} onPress={(number)=>{setChildState(number)}} />) | |
const awesomeChildListKeyExtractor = (item)=>(`awesome-child-key-${item.id}`); | |
return( | |
<FlatList | |
renderItem={awesomeChildListRenderItem} | |
keyExtractor={awesomeChildListKeyExtractor} | |
/>) | |
} |
那么现在应用程序的性能好多了?嗯!还没有,但我们正在取得一些进展。现在我们使组件更加清晰易读。
2. 正确使用useCallback
♻️。
任何用于重新渲染组件的函数,
都不应导致不必要的重新渲染,无论何时,只要你不想,无论在 jsx 元素中使用哪个函数,都可以像上一步所示那样将其调出,并应用我们的 hero hook,useCallback
它会做什么?这也不是本文要讨论的内容,你可以在许多文档和文章中找到。但我将仅展示如何实现它来提升 React-native 的性能。
例子
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
export default ()=>{ | |
const [childState, setChildState] = useState(0); | |
//using useCallback | |
const awesomeChildListRenderItem = useCallback( | |
({ item }) => ( | |
<AwesomeChild {...item} onPress={(number)=>{setChildState(number)}} /> | |
),[]); | |
const awesomeChildListKeyExtractor = useCallback( (item) => `awesome-child-key-${item.id}`,[]); | |
return( | |
<FlatList | |
renderItem={awesomeChildListRenderItem} | |
keyExtractor={awesomeChildListKeyExtractor} | |
/> | |
) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
export default ()=>{ | |
const [childState, setChildState] = useState(0); | |
//using useCallback | |
const awesomeChildListRenderItem = useCallback( | |
({ item }) => ( | |
<AwesomeChild {...item} onPress={(number)=>{setChildState(number)}} /> | |
),[]); | |
const awesomeChildListKeyExtractor = useCallback( (item) => `awesome-child-key-${item.id}`,[]); | |
return( | |
<FlatList | |
renderItem={awesomeChildListRenderItem} | |
keyExtractor={awesomeChildListKeyExtractor} | |
/> | |
) | |
} |
现在,我们通过应用 hero hook 取得了一些进展。这确保了 FlatList 不会重新渲染,因为它
AwesomeChild
已被记住,并且没有任何东西可以改变它的值。
你可以从上面的代码中观察到,在我们用 useCallback 包装的旧函数之后紧接着使用了空括号,这是依赖关系,如果函数正在使用任何其他值,则需要使用空括号。
例子
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
export default ()=>{ | |
//Sates is 0 initially | |
const [pressCount, setPressCount] = useState(0); | |
//Function to update last state by +1 | |
const updateButtonPress = useCallback(() => { | |
setPressCount(pressCount + 1); | |
}, []); | |
//Empty dependancy | |
return( | |
<Button onPress={updateButtonPress} title="Add 1" /> | |
) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
export default ()=>{ | |
//Sates is 0 initially | |
const [pressCount, setPressCount] = useState(0); | |
//Function to update last state by +1 | |
const updateButtonPress = useCallback(() => { | |
setPressCount(pressCount + 1); | |
}, []); | |
//Empty dependancy | |
return( | |
<Button onPress={updateButtonPress} title="Add 1" /> | |
) | |
} |
上面的代码是将 1 添加到其最后一个状态,但它始终设置为 1,因为 useCallback
pressCount
在第一次渲染时将状态记住为 0,每当我们使用 useCallback 中的状态时它始终为 0,所以每次我们按下时,它将是 0+1 = 1。要获取最新值,我们需要将状态添加到 useCallback 内的空数组中。即,useCallback(()=>{...},[pressCount])
所以找到依赖项并填充有点头疼,我知道对吧!?为此,您只需配置 eslint 和 eslint-react-hook,之后 VS 代码将处理它。
在应用之前
useCallback

注意激活选项卡的滞后。
应用后
useCallback

3.memo
对于整个组件🚦。
用于export default React.memo(AwesomeChild)
导出几乎所有的组件,这与 React Class Turf 中的 PureComponent 非常相似。它通过比较上一个和下一个 props 来防止组件的重新渲染,有时它会允许渲染一些不需要的 props 更改,为了提高限制,我们可以使用 areEqual 函数作为React.memo
函数的第二个参数,
例子
非限制性备忘录
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
const AwesomeChild =({text,style})=>{ | |
return( | |
<Text style={style} >{text}</Text> | |
) | |
} | |
export default React.memo(AwesomeChild) | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
const AwesomeChild =({text,style})=>{ | |
return( | |
<Text style={style} >{text}</Text> | |
) | |
} | |
export default React.memo(AwesomeChild) | |
限制性备忘录
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
const AwesomeChild =({text,style})=>{ | |
return( | |
<Text style={style} >{text}</Text> | |
) | |
} | |
const areEqual=(prevProps,nextProps)=>{ | |
// return false prevProps.text & nextProps.text are not equal. | |
return prevProps.text===nextProps.text | |
// else all are equal, no re-render | |
return true | |
} | |
export default React.memo(AwesomeChild,areEqual) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
const AwesomeChild =({text,style})=>{ | |
return( | |
<Text style={style} >{text}</Text> | |
) | |
} | |
const areEqual=(prevProps,nextProps)=>{ | |
// return false prevProps.text & nextProps.text are not equal. | |
return prevProps.text===nextProps.text | |
// else all are equal, no re-render | |
return true | |
} | |
export default React.memo(AwesomeChild,areEqual) |
在此,仅当父组件的 prop 发生变化时,组件才会重新渲染,如果prop 发生变化,
text
则不会重新渲染。 (在大多数情况下,正常的备忘录可以正常工作) style
4.其他提示🔧。
- 配置 VS Codes <-> eslint 以编写最佳代码并使用 prettifier 使您的代码整洁干净。
- 保持导入清洁,删除未使用的导入,(eslint 将通过显示它来帮助您)。
- 不要使用内联样式,最大限度地利用 StyleSheet(eslint 将通过显示它来帮助您)。
- 小心使用
useEffect
,并结合适当的依赖性和条件来执行操作。 - 要找出导致浪费重新渲染的原因 - 使用这个不错的包,安装为开发依赖项https://github.com/welldone-software/why-did-you-render。
- 我的.eslint.js:https://gist.github.com/ltsharma/cc88c8f96536f656570b23586b4e3098
- 我的开发依赖:https://gist.github.com/ltsharma/04344dbb530d65eb1341b415e5df5962
希望这篇文章能对大家有所帮助。我花了好几个小时才找到答案,希望能分享给大家,毕竟在网上不容易找到。
欢迎留言分享你的想法和技巧……