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