詳解Stack?Navigator中使用自定義的Render?Callback
Stack Navigator使用component props傳遞組件
通常來(lái)說(shuō),Stack Navigator的默認(rèn)用法,是這樣的
<NavigationContainer>\ <Stack.Navigator>\ <Stack.Screen name="Home" component={HomeScreen} />\ </Stack.Navigator>\ </NavigationContainer>
自定義的組件HomeScreen
是作為component
屬性,傳遞給Stack.Screen
的。這種默認(rèn)的做法,會(huì)讓Stack.Screen
對(duì)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
時(shí),避免組件重復(fù)渲染的工作,就移交給了使用者。render callback
通常是為了傳遞extra props
,但是優(yōu)化方式和extra props
是沒(méi)什么關(guān)系的,以下的例子中,為了避免干擾,沒(méi)有新引入extra props
,只是用stack navigator
傳遞給組件的默認(rèn)屬性來(lái)舉例子。
為了更好的監(jiān)控,HomeScreen
是否被重復(fù)渲染,在代碼中打印了一個(gè)隨機(jī)數(shù),便于觀察日志輸出。
無(wú)因素引起組件更新時(shí),使用render callback的效果
下面這段代碼,使用了render callback來(lái)渲染HomeScreen。
const homeInst = (props) => (<HomeScreen {...props} />)
運(yùn)行起來(lái)的效果和不使用render callback的效果是一樣的。在頻繁的HomeScreen和DetailsScreen切換過(guò)程中,因?yàn)闆](méi)有引起HomeScreen重繪的因素存在,所以HomeScreen并沒(méi)有被重復(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
有因素引起組件更新時(shí),使用component props的效果
為了引起HomeScreen
組件的更新,以便驗(yàn)證Screen Navigator
是否對(duì)HomeScreen做了避免重復(fù)渲染的優(yōu)化,在代碼中加入了一個(gè)新的狀態(tài)age
,當(dāng)點(diǎn)擊Button
時(shí),這個(gè)age
不斷的自增1,因?yàn)?code>App里有state
的更新,所以作為父組件的App
會(huì)更新,而作為子組件的HomeScreen
通常意義上(不通常的情況下,就是使用了React.memo
等優(yōu)化手段)說(shuō),也會(huì)重新渲染。因?yàn)檫@就是React
的重繪機(jī)制:從父組件開(kāi)始,一層一層向下重繪。
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)我點(diǎn)擊Button
后,發(fā)現(xiàn)HomeScreen
并沒(méi)有重繪,所以當(dāng)使用component props
傳遞組件時(shí),Stack Navigator
確實(shí)是做了防止不必要重繪的優(yōu)化。
具體效果可以參考下面的動(dòng)畫:
有因素引起組件更新時(shí),使用render callback的效果
那么在上面所說(shuō)的場(chǎng)景下,用render callback
會(huì)怎么樣呢?答案顯而易見(jiàn),如果沒(méi)有做任何優(yōu)化處理,那么HomeScreen的不必要的重復(fù)渲染,是無(wú)法避免的了。
代碼如下:
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)畫效果如下:
可以看到,當(dāng)我點(diǎn)擊Button
改變App
的狀態(tài)時(shí),本來(lái)沒(méi)有必要變化的HomeScreen
,就瘋狂的重繪了起來(lái),當(dāng)然每次重繪的結(jié)果,都和之前一樣,這就是無(wú)效的重繪,我們應(yīng)該避免。
有因素引起組件更新時(shí),在render callback中使用React.memo
根據(jù)上面官網(wǎng)文檔給出的提示,如果想避免重繪,應(yīng)該用React.memo
(因?yàn)楦杏X(jué)FB已經(jīng)全面擁抱Hook了,所以這里也不考慮PureComponent了)來(lái)包裝你的組件。
const MemoHomeScreen = React.memo(HomeScreen)
說(shuō)一百句,也頂不上一句代碼,具體代碼如下(都是可以copy到你的環(huán)境中直接運(yù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;
上面這段代碼的運(yùn)行效果,和使用component props
傳遞HomeScreen
的運(yùn)行效果一樣。只不過(guò)前者是使用者自己優(yōu)化了重繪,后者是Stack Navigator
替你優(yōu)化了。
有因素引起組件更新時(shí),在render callback中使用useCallback
如果我們?cè)偕晕⒍嘞胍幌拢?code>hostInst本質(zhì)上是一個(gè)function
,而說(shuō)道function
的避免重復(fù)計(jì)算的手段,自然想到了useCallback
。我用useCallback
來(lái)包裝一下,看看是否能達(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á)到避免無(wú)效重復(fù)繪制HomeScreen
的目的。
總結(jié)
Stack Navigator
的使用,除非特殊情況,非得加extraData
,否則強(qiáng)烈推薦用props
的方式傳遞組件,減少思維負(fù)擔(dān)。如果要使用render callback
,那么我是推薦使用useCallback
代替React.memo
的,因?yàn)榕浜?code>useCallback的第二個(gè)參數(shù),控制起來(lái)更加有針對(duì)性。
以上就是詳解Stack Navigator中使用自定義的Render Callback的詳細(xì)內(nèi)容,更多關(guān)于Stack Navigator自定義Render Callback的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
通過(guò)實(shí)例學(xué)習(xí)React中事件節(jié)流防抖
這篇文章主要介紹了通過(guò)實(shí)例學(xué)習(xí)React中事件節(jié)流防抖,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,,需要的朋友可以參考下2019-06-06React前端開(kāi)發(fā)createElement源碼解讀
這篇文章主要為大家介紹了React前端開(kāi)發(fā)createElement源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11React Native 使用Fetch發(fā)送網(wǎng)絡(luò)請(qǐng)求的示例代碼
本篇文章主要介紹了React Native 使用Fetch發(fā)送網(wǎng)絡(luò)請(qǐng)求的示例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12react 應(yīng)用多入口配置及實(shí)踐總結(jié)
這篇文章主要介紹了react 應(yīng)用多入口配置及實(shí)踐總結(jié),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-10-10React系列useSyncExternalStore學(xué)習(xí)詳解
這篇文章主要為大家介紹了React系列useSyncExternalStore的學(xué)習(xí)及示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07react如何同步獲取useState的最新?tīng)顟B(tài)值
這篇文章主要介紹了react如何同步獲取useState的最新?tīng)顟B(tài)值問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01React中Refs的使用場(chǎng)景及核心要點(diǎn)詳解
在使用?React?進(jìn)行開(kāi)發(fā)過(guò)程中,或多或少使用過(guò)?Refs?進(jìn)行?DOM?操作,這篇文章主要介紹了?Refs?功能和使用場(chǎng)景以及注意事項(xiàng),希望對(duì)大家有所幫助2023-07-07React?split實(shí)現(xiàn)分割字符串的使用示例
當(dāng)我們需要將一個(gè)字符串按照指定的分隔符進(jìn)行分割成數(shù)組時(shí),我們可以在組件的生命周期方法中使用split方法來(lái)實(shí)現(xiàn)這個(gè)功能,本文就來(lái)介紹一下,感興趣的可以了解下2023-10-10