網站首頁 編程語言 正文
Stack Navigator使用component props傳遞組件
通常來說,Stack Navigator的默認用法,是這樣的
<NavigationContainer>\ <Stack.Navigator>\ <Stack.Screen name="Home" component={HomeScreen} />\ </Stack.Navigator>\ </NavigationContainer>
自定義的組件HomeScreen
是作為component
屬性,傳遞給Stack.Screen
的。這種默認的做法,會讓Stack.Screen
對Screen Component進行優化,避免了很多不必要的渲染。官方文檔中,是這樣描述的。
Note: By default, React Navigation applies optimizations to screen components to prevent unnecessary renders. Using a render callback removes those optimizations. So if you use a render callback, you'll need to ensure that you use?React.memo?or?React.PureComponent?for your screen components to avoid performance issues.
從這段話中,我們可以看出,當使用自定義的render callback
時,避免組件重復渲染的工作,就移交給了使用者。render callback
通常是為了傳遞extra props
,但是優化方式和extra props
是沒什么關系的,以下的例子中,為了避免干擾,沒有新引入extra props
,只是用stack navigator
傳遞給組件的默認屬性來舉例子。
為了更好的監控,HomeScreen
是否被重復渲染,在代碼中打印了一個隨機數,便于觀察日志輸出。
無因素引起組件更新時,使用render callback的效果
下面這段代碼,使用了render callback來渲染HomeScreen。
const homeInst = (props) => (<HomeScreen {...props} />)
運行起來的效果和不使用render callback的效果是一樣的。在頻繁的HomeScreen和DetailsScreen切換過程中,因為沒有引起HomeScreen重繪的因素存在,所以HomeScreen并沒有被重復渲染。
import React from 'react' import { View, Text, Button } from 'react-native' import { NavigationContainer } from '@react-navigation/native' import { createNativeStackNavigator } from '@react-navigation/native-stack' function HomeScreen({ navigation }) { console.log(`home: ${Math.random(new Date().getTime())}`) const goToDetail = () => { navigation.navigate('Details') } return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Home Screen</Text> <Button title='Go To Detail' onPress={goToDetail}></Button> </View> ) } function DetailsScreen({ navigation }) { const goHome = () => { navigation.navigate('Home') } return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Details Screen</Text> <Button title='Go Home' onPress={goHome}></Button> </View> ) } const Stack = createNativeStackNavigator() function App() { const homeInst = (props) => (<HomeScreen {...props} />) return ( <NavigationContainer> <Stack.Navigator initialRouteName='Home'> <Stack.Screen name='Home'> {homeInst} </Stack.Screen> <Stack.Screen name='Details' component={DetailsScreen}/> </Stack.Navigator> </NavigationContainer> ) } export default App
有因素引起組件更新時,使用component props的效果
為了引起HomeScreen
組件的更新,以便驗證Screen Navigator
是否對HomeScreen做了避免重復渲染的優化,在代碼中加入了一個新的狀態age
,當點擊Button
時,這個age
不斷的自增1,因為App
里有state
的更新,所以作為父組件的App
會更新,而作為子組件的HomeScreen
通常意義上(不通常的情況下,就是使用了React.memo
等優化手段)說,也會重新渲染。因為這就是React
的重繪機制:從父組件開始,一層一層向下重繪。
import React, {useState} from 'react' import { View, Text, Button } from 'react-native' import { NavigationContainer } from '@react-navigation/native' import { createNativeStackNavigator } from '@react-navigation/native-stack' function HomeScreen({ navigation }) { console.log(`home: ${Math.random(new Date().getTime())}`) const goToDetail = () => { navigation.navigate('Details') } return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Home Screen</Text> <Button title='Go To detail' onPress={goToDetail}></Button> </View> ) } function DetailsScreen({ navigation }) { const goHome = () => { navigation.navigate('Home') } return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Details Screen</Text> <Button title='Go Home' onPress={goHome}></Button> </View> ) } const Stack = createNativeStackNavigator() function App() { const [age, setAge] = useState(20) return ( <NavigationContainer> <Stack.Navigator initialRouteName='Home'> <Stack.Screen name='Home' component={HomeScreen} /> <Stack.Screen name='Details' component={DetailsScreen} /> </Stack.Navigator> <View> <Text>{age}</Text> <Button title='Increase Age' onPress={() => (setAge(age + 1))}></Button> </View> </NavigationContainer> ) } export default App
當我點擊Button
后,發現HomeScreen
并沒有重繪,所以當使用component props
傳遞組件時,Stack Navigator
確實是做了防止不必要重繪的優化。
具體效果可以參考下面的動畫:
有因素引起組件更新時,使用render callback的效果
那么在上面所說的場景下,用render callback
會怎么樣呢?答案顯而易見,如果沒有做任何優化處理,那么HomeScreen的不必要的重復渲染,是無法避免的了。
代碼如下:
import React, { useState } from 'react' import { View, Text, Button } from 'react-native' import { NavigationContainer } from '@react-navigation/native' import { createNativeStackNavigator } from '@react-navigation/native-stack' function HomeScreen({ navigation }) { console.log(`home: ${Math.random(new Date().getTime())}`) const goToDetail = () => { navigation.navigate('Details') } return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Home Screen</Text> <Button title='Go To detail' onPress={goToDetail}></Button> </View> ) } function DetailsScreen({ navigation }) { const goHome = () => { navigation.navigate('Home') } return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Details Screen</Text> <Button title='Go Home' onPress={goHome}></Button> </View> ) } const Stack = createNativeStackNavigator() function App() { const [age, setAge] = useState(20) const homeInst = (props) => (<HomeScreen {...props} />) return ( <NavigationContainer> <Stack.Navigator initialRouteName='Home'> <Stack.Screen name='Home'> {homeInst} </Stack.Screen> <Stack.Screen name='Details' component={DetailsScreen} /> </Stack.Navigator> <View> <Text>{age}</Text> <Button title='Increase Age' onPress={() => (setAge(age + 1))}></Button> </View> </NavigationContainer> ) } export default App
動畫效果如下:
可以看到,當我點擊Button
改變App
的狀態時,本來沒有必要變化的HomeScreen
,就瘋狂的重繪了起來,當然每次重繪的結果,都和之前一樣,這就是無效的重繪,我們應該避免。
有因素引起組件更新時,在render callback中使用React.memo
根據上面官網文檔給出的提示,如果想避免重繪,應該用React.memo
(因為感覺FB已經全面擁抱Hook了,所以這里也不考慮PureComponent了)來包裝你的組件。
const MemoHomeScreen = React.memo(HomeScreen)
說一百句,也頂不上一句代碼,具體代碼如下(都是可以copy到你的環境中直接運行的):
import React, {useState} from 'react'; import { View, Text, Button } from 'react-native'; import { NavigationContainer } from '@react-navigation/native'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; function HomeScreen({ navigation }) { console.log(`home: ${Math.random(new Date().getTime())}`) const goToDetail = () => { navigation.navigate('Details') } return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Home Screen</Text> <Button title='Go To detail' onPress={goToDetail}></Button> </View> ) } function DetailsScreen({ navigation }) { const goHome = () => { navigation.navigate('Home') } return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Details Screen</Text> <Button title='Go Home' onPress={goHome}></Button> </View> ) } const Stack = createNativeStackNavigator() const MemoHomeScreen = React.memo(HomeScreen) function App() { const [age, setAge] = useState(20) const homeInst = (props) => (<MemoHomeScreen {...props} />) return ( <NavigationContainer> <Stack.Navigator initialRouteName='Home'> <Stack.Screen name='Home'> {homeInst} </Stack.Screen> <Stack.Screen name='Details' component={DetailsScreen} /> </Stack.Navigator> <View> <Text>{age}</Text> <Button title='Increase Age' onPress={() => (setAge(age + 1))}></Button> </View> </NavigationContainer> ) } export default App;
上面這段代碼的運行效果,和使用component props
傳遞HomeScreen
的運行效果一樣。只不過前者是使用者自己優化了重繪,后者是Stack Navigator
替你優化了。
有因素引起組件更新時,在render callback中使用useCallback
如果我們再稍微多想一下,hostInst
本質上是一個function
,而說道function
的避免重復計算的手段,自然想到了useCallback
。我用useCallback
來包裝一下,看看是否能達到一樣的效果:
const homeInst = useCallback((props) => (<HomeScreen {...props} />), [])
完整代碼如下:
// In App.js in a new project import React, {useState, useCallback} from 'react' import { View, Text, Button } from 'react-native' import { NavigationContainer } from '@react-navigation/native' import { createNativeStackNavigator } from '@react-navigation/native-stack' function HomeScreen({ navigation }) { console.log(`home: ${Math.random(new Date().getTime())}`) const goToDetail = () => { navigation.navigate('Details') } return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Home Screen</Text> <Button title='Go To detail' onPress={goToDetail}></Button> </View> ) } function DetailsScreen({ navigation }) { const goHome = () => { navigation.navigate('Home') } return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Details Screen</Text> <Button title='Go Home' onPress={goHome}></Button> </View> ) } const Stack = createNativeStackNavigator() function App() { const [age, setAge] = useState(20) const homeInst = useCallback((props) => (<HomeScreen {...props} />), []) return ( <NavigationContainer> <Stack.Navigator initialRouteName='Home'> <Stack.Screen name='Home'> {homeInst} </Stack.Screen> <Stack.Screen name='Details' component={DetailsScreen} /> </Stack.Navigator> <View> <Text>{age}</Text> <Button title='Increase Age' onPress={() => (setAge(age + 1))}></Button> </View> </NavigationContainer> ) } export default App
我試了一下,效果和使用React.memo
是一樣的,都可以達到避免無效重復繪制HomeScreen
的目的。
總結
Stack Navigator
的使用,除非特殊情況,非得加extraData
,否則強烈推薦用props
的方式傳遞組件,減少思維負擔。如果要使用render callback
,那么我是推薦使用useCallback
代替React.memo
的,因為配合useCallback
的第二個參數,控制起來更加有針對性。
原文鏈接:https://juejin.cn/post/7149085777291378695
相關推薦
- 2023-01-03 Android?自定義Livedata使用示例解析_Android
- 2023-07-26 TypeScript中的類型聲明declare
- 2022-09-14 jQuery實現簡易計算器功能_jquery
- 2022-11-02 Android?LeakCanary的使用方法介紹_Android
- 2022-05-21 python?判斷文件或文件夾是否存在_python
- 2022-09-13 C++中string使用+號與int拼接方式_C 語言
- 2023-03-29 Python中命令行參數argparse模塊的使用_python
- 2023-01-09 初識C++?Vector模板與實例化原理_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支