美文网首页
React.memo, useMemo, useCallback

React.memo, useMemo, useCallback

作者: 小冰山口 | 来源:发表于2024-01-12 17:21 被阅读0次

    当遇到一个需求, 每次渲染一个页面时, 如果数据源没有发生变化, 实际上页面是不用重新渲染的, 但是在RN中, 有没有一个方法是可以去满足这个需求的呢?

    在函数式组件中:
    需要用到React.memo
    以下是原始的代码:

     export default withFloatingButton(
        (props: Props) => {
          console.log('render...')
          const theme = useContext(ThemeContext)
          const styles = theme === 'dark' ? darkStyles : lightStyles
          return (
            <View style={styles.avarta}>
              <Image 
                style={styles.img}
                source={{uri: `${props.info.avarta}`}}
              >
              </Image>
              <Text style={styles.titleTxt}>
                {`${props.info.name}`}
              </Text>
              <View style={styles.descContent}>
                <Text style={styles.descTxt}>
                  {`${props.info.desc}`}
                </Text>
              </View>
            </View>
          )
        }
      )
    

    但这样写的话, 如果props的值重新设置的话, 这个组件就会重新渲染一遍, 但是, 重新设置的props的值事实上并不一定是变化的. 那这时候重新渲染就没有意义了, 如果这时候用React.memo包一层的话, 情况就会不同:

    export default React.memo(
      withFloatingButton(
        (props: Props) => {
          console.log('render...')
          const theme = useContext(ThemeContext)
          const styles = theme === 'dark' ? darkStyles : lightStyles
          return (
            <View style={styles.avarta}>
              <Image 
                style={styles.img}
                source={{uri: `${props.info.avarta}`}}
              >
              </Image>
              <Text style={styles.titleTxt}>
                {`${props.info.name}`}
              </Text>
              <View style={styles.descContent}>
                <Text style={styles.descTxt}>
                  {`${props.info.desc}`}
                </Text>
              </View>
            </View>
          )
        }
      )
    , (preProps: Props, nextProps: Props) => {
      return JSON.stringify(preProps.info) === JSON.stringify(nextProps.info)
    })
    

    这段代码的意思是, 将原先的整个函数, 作为参数传进入React.memo的第一个参数, 那第二个参数也是一个函数:

    (preProps: any, nextProps: any) => {
      return xxx /// bool类型
    }
    

    这个函数的意思是将原先的preProps和新的nextProps进行比较, 来决定是否渲染, return的这个返回值, 如果是true(表示数据一样), 则不重新渲染, 如果是false(表示数据不同), 需要重新渲染.

    那么在类组件中有没有类似的方法呢?
    也是有的

    shouldComponentUpdate(nextProps: Readonly<Props>): boolean
    

    需要去实现这个方法, 在这个方法中, 有点类似于React.memo的第二个参数, 需要是当前的this.props和新的nextProps进行比较:

      shouldComponentUpdate(nextProps: Readonly<Props>): boolean {
        return JSON.stringify(nextProps.info) !== JSON.stringify(this.props.info)
      }
    

    区别在于, 当两者不相同时, 则返回true, 表示需要重新渲染. 当两者相同时, 则返回false, 表示不需要重新渲染. 这是跟React.memo刚好相反的地方.

    好了, 现在来聊一下如何使用useMemo
    假设现在有这样一个场景

    image.png
    需要算出列表的某一项的总计, 那么事实上, 这个列表如何展示, 其实跟这个计算并没有什么关系, 比如, 列表也可以这样展示
    image.png
    但是, 如果常规的写法的话, 就会将数值再重新计算一遍, 那这个其实是很没有必要的, 试想一下, 假设这个列表有几千条数据呢? 所以, 我们需要将这个列表的数据缓存起来, 这个就用到了useMemo

    使用前:

       const calculateAmount = () => {
         return data.map((item) => item.amount).reduce((pre, cur) => pre + cur)
       }
    

    使用后:

      // useMemo
      const calculateAmount = useMemo(
        () => {
        return data.map((item) => item.amount).reduce((pre, cur) => pre + cur)
      }, [data])
    

    但其实上面的还不够完善, 我们仅仅只是数据需要缓存吗? 其实整个底部合计的view都是可以缓存的. 因为当数据没有发生变化时, 整个view都是不变的.

    那么就可以写成这样:

      // useMemo
      const totalAmountView = useMemo(
        () => {
          const totalAmount = data.map((item) => item.amount).reduce((pre, cur) => pre + cur)
          return (
            <View style={styles.bottomView}>
              <Text style={styles.titleTxt}>
                {`${totalAmount}`}
              </Text>
              <Text style={styles.titleTxt}>
                {`费用总计: `}
              </Text>
            </View>
          )
        }
      , [data])
    

    最后我们来聊一下useCallback
    比如在上面的列表中, 其实cell的点击事件是并不需要每次都创建一遍的. 那么如何缓存这些点击事件的函数呢, 这时候就要用到useCallback了.

      const didClickCell = useCallback(
        (item: UserData, index: number) => {
          console.log(`点击了第${index + 1}个cell, 名称是${item.name}`)
        }
      , [])
    

    这样, 在onPress属性中就可以这样写:

            onPress={() => {
              didClickCell(item, index)
            }}
    

    那可以直接在属性中写一个函数吗? 也是可以的. 这时, useCallback就要改一下, 改成一个返回值是一个函数的写法:

      const didClickCellNew = useCallback(
        (item: UserData, index: number) => () => {
          console.log(`点击了第${index + 1}个cell, 名称是${item.name}`)
        }
      , [])
    

    这样, 在填写onPress属性时, 就可以直接写函数了:

    onPress={ didClickCellNew(item, index) }
    

    相关文章

      网友评论

          本文标题:React.memo, useMemo, useCallback

          本文链接:https://www.haomeiwen.com/subject/cmuxodtx.html