详解Stack Navigator中使用自定义的Render Callback

 更新时间:2022年10月12日 08:58:07   作者:前百花真君  
这篇文章主要为大家介绍了Stack Navigator中使用自定义的Render Callback使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

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的第二个参数,控制起来更加有针对性。

以上就是详解Stack Navigator中使用自定义的Render Callback的详细内容,更多关于Stack Navigator自定义Render Callback的资料请关注脚本之家其它相关文章!

相关文章

  • 使用React实现轮播效果组件示例代码

    使用React实现轮播效果组件示例代码

    React刚出来不久,组件还比较少,不像jquery那样已经有很多现成的插件了,于是自己写了一个基于React的轮播效果组件,现在分享给大家,有需要的可以参考借鉴。
    2016-09-09
  • react 国际化的实现代码示例

    react 国际化的实现代码示例

    这篇文章主要介绍了react 国际化的实现代码示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-09-09
  • React中的for循环解读

    React中的for循环解读

    这篇文章主要介绍了React中的for循环解读,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-01-01
  • React组件创建与事件绑定的实现方法

    React组件创建与事件绑定的实现方法

    react事件绑定时。this并不会指向当前DOM元素。往往使用bind来改变this指向,今天通过本文给大家介绍React事件绑定的方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2022-12-12
  • React避免不必要的重新渲染的方法示例

    React避免不必要的重新渲染的方法示例

    构建高性能 React 应用程序的关键之一是避免不必要的重新渲染,React 的渲染引擎是高效的,但防止在不需要的地方重新渲染仍然至关重要,在这篇文章中,我们将介绍常见错误以及如何避免它们,需要的朋友可以参考下
    2024-10-10
  • React用法之高阶组件的用法详解

    React用法之高阶组件的用法详解

    高阶组件也就是我们常说的HOC,是React中用于复用组件逻辑的一种高级技巧。这篇文章主要通过一些示例带大家学习一下高阶组件的使用,希望对大家有所帮助
    2023-04-04
  • 在create-react-app中使用sass的方法示例

    在create-react-app中使用sass的方法示例

    这篇文章主要介绍了在create-react-app中使用sass的方法示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-10-10
  • React深入浅出分析Hooks源码

    React深入浅出分析Hooks源码

    在react类组件(class)写法中,有setState和生命周期对状态进行管理,但是在函数组件中不存在这些,故引入hooks(版本:>=16.8),使开发者在非class的情况下使用更多react特性
    2022-11-11
  • react父组件调用子组件的方式汇总

    react父组件调用子组件的方式汇总

    在react中常用props实现子组件数据到父组件的传递,但是父组件调用子组件的功能却不常用,下面这篇文章主要给大家介绍了关于react父组件调用子组件的相关资料,需要的朋友可以参考下
    2022-08-08
  • React为什么需要Scheduler调度器原理详解

    React为什么需要Scheduler调度器原理详解

    这篇文章主要为大家介绍了React为什么需要Scheduler调度器原理详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10

最新评论