網(wǎng)站首頁 編程語言 正文
Stack Navigator使用component props傳遞組件
通常來說,Stack Navigator的默認(rèn)用法,是這樣的
<NavigationContainer>\ <Stack.Navigator>\ <Stack.Screen name="Home" component={HomeScreen} />\ </Stack.Navigator>\ </NavigationContainer>
自定義的組件HomeScreen
是作為component
屬性,傳遞給Stack.Screen
的。這種默認(rèn)的做法,會讓Stack.Screen
對Screen Component進(jìn)行優(yōu)化,避免了很多不必要的渲染。官方文檔中,是這樣描述的。
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.
從這段話中,我們可以看出,當(dāng)使用自定義的render callback
時,避免組件重復(fù)渲染的工作,就移交給了使用者。render callback
通常是為了傳遞extra props
,但是優(yōu)化方式和extra props
是沒什么關(guān)系的,以下的例子中,為了避免干擾,沒有新引入extra props
,只是用stack navigator
傳遞給組件的默認(rèn)屬性來舉例子。
為了更好的監(jiān)控,HomeScreen
是否被重復(fù)渲染,在代碼中打印了一個隨機數(shù),便于觀察日志輸出。
無因素引起組件更新時,使用render callback的效果
下面這段代碼,使用了render callback來渲染HomeScreen。
const homeInst = (props) => (<HomeScreen {...props} />)
運行起來的效果和不使用render callback的效果是一樣的。在頻繁的HomeScreen和DetailsScreen切換過程中,因為沒有引起HomeScreen重繪的因素存在,所以HomeScreen并沒有被重復(fù)渲染。
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做了避免重復(fù)渲染的優(yōu)化,在代碼中加入了一個新的狀態(tài)age
,當(dāng)點擊Button
時,這個age
不斷的自增1,因為App
里有state
的更新,所以作為父組件的App
會更新,而作為子組件的HomeScreen
通常意義上(不通常的情況下,就是使用了React.memo
等優(yōu)化手段)說,也會重新渲染。因為這就是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
當(dāng)我點擊Button
后,發(fā)現(xiàn)HomeScreen
并沒有重繪,所以當(dāng)使用component props
傳遞組件時,Stack Navigator
確實是做了防止不必要重繪的優(yōu)化。
具體效果可以參考下面的動畫:
有因素引起組件更新時,使用render callback的效果
那么在上面所說的場景下,用render callback
會怎么樣呢?答案顯而易見,如果沒有做任何優(yōu)化處理,那么HomeScreen的不必要的重復(fù)渲染,是無法避免的了。
代碼如下:
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
動畫效果如下:
可以看到,當(dāng)我點擊Button
改變App
的狀態(tài)時,本來沒有必要變化的HomeScreen
,就瘋狂的重繪了起來,當(dāng)然每次重繪的結(jié)果,都和之前一樣,這就是無效的重繪,我們應(yīng)該避免。
有因素引起組件更新時,在render callback中使用React.memo
根據(jù)上面官網(wǎng)文檔給出的提示,如果想避免重繪,應(yīng)該用React.memo
(因為感覺FB已經(jīng)全面擁抱Hook了,所以這里也不考慮PureComponent了)來包裝你的組件。
const MemoHomeScreen = React.memo(HomeScreen)
說一百句,也頂不上一句代碼,具體代碼如下(都是可以copy到你的環(huán)境中直接運行的):
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
的運行效果一樣。只不過前者是使用者自己優(yōu)化了重繪,后者是Stack Navigator
替你優(yōu)化了。
有因素引起組件更新時,在render callback中使用useCallback
如果我們再稍微多想一下,hostInst
本質(zhì)上是一個function
,而說道function
的避免重復(fù)計算的手段,自然想到了useCallback
。我用useCallback
來包裝一下,看看是否能達(dá)到一樣的效果:
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
是一樣的,都可以達(dá)到避免無效重復(fù)繪制HomeScreen
的目的。
總結(jié)
Stack Navigator
的使用,除非特殊情況,非得加extraData
,否則強烈推薦用props
的方式傳遞組件,減少思維負(fù)擔(dān)。如果要使用render callback
,那么我是推薦使用useCallback
代替React.memo
的,因為配合useCallback
的第二個參數(shù),控制起來更加有針對性。
原文鏈接:https://juejin.cn/post/7149085777291378695
相關(guān)推薦
- 2022-09-19 ASP.NET?Core模仿中間件方式實現(xiàn)列表過濾功能_實用技巧
- 2022-03-19 使用Docker部署Spring?Boot項目的實現(xiàn)步驟_docker
- 2022-10-20 利用Python的tkinter模塊實現(xiàn)界面化的批量修改文件名_python
- 2022-08-21 C語言實現(xiàn)隊列的示例詳解_C 語言
- 2023-02-09 C++命名空間?缺省參數(shù)?const總結(jié)?引用總結(jié)?內(nèi)聯(lián)函數(shù)?auto關(guān)鍵字詳解_C 語言
- 2022-09-22 集合Map以及HashMap
- 2022-03-27 C++如何用數(shù)組模擬鏈表_C 語言
- 2023-01-21 Go語言O(shè)RM框架構(gòu)造查詢條件示例詳解_Golang
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支