React Native Ref转发/Memo缓存/HOC高阶组件/Context上下文

一、ref 转发解决什么问题

1、使用自定义组件时,实现外层组件对原始组件(TextInput)的操作

外层组件使用 ref 属性

React Native Ref转发/Memo缓存/HOC高阶组件/Context上下文_第1张图片

子组件使用 forwardRef 包裹

React Native Ref转发/Memo缓存/HOC高阶组件/Context上下文_第2张图片

2、函数式组件对外暴露实例方法(cusomFocus)

子组件

React Native Ref转发/Memo缓存/HOC高阶组件/Context上下文_第3张图片

父组件如图一所示

二、memo 解决什么问题?

1 、 避免多余渲染

问题:每次点击按钮都会导致 InfoView 组件发生重绘,即使每次 setInfo 都是一样的内容也是如此,这就是多余的

React Native Ref转发/Memo缓存/HOC高阶组件/Context上下文_第4张图片

优化:给函数式组件 InfoView 外面包裹上 React.memo() ,比较前后 props 的值来决定是否发生重绘。

React Native Ref转发/Memo缓存/HOC高阶组件/Context上下文_第5张图片

2、避免重复计算、重复创建对象

1、useMemo 缓存数据

在没有使用useMemo 时,仅改变组件中 showType 的状态,会导致组件重绘重新计算合计,而状态的变更并不会导致合计的计算结果发生变化,此时的计算就是多余的,就可以使用 useMemo 缓存函数计算结果,仅依赖条件 data 发生变化时才重新计算。useMemo 返回值是一个值而不是一个方法。

React Native Ref转发/Memo缓存/HOC高阶组件/Context上下文_第6张图片

2、useMemo 缓存 UI 渲染

在合计这个 UI 模块,当数据源 data 不会发生改变的情况下,整个 UI 结构也是固定的,就可以直接缓存整个组件。

React Native Ref转发/Memo缓存/HOC高阶组件/Context上下文_第7张图片

3、useCallback 缓存回调函数

问题:

  • 在 1 处如果 view 组件重新渲染一次,每次都是会生成一个新的 onItemPress 函数对象。
  • 在 2 处列表每生成一个 Item 组件就都会创建一个新的函数对象。
  • 这些都是多余,没必要,无意义的操作。

React Native Ref转发/Memo缓存/HOC高阶组件/Context上下文_第8张图片

优化:使用 useCallback 解决两层重复创建函数对象的问题

React Native Ref转发/Memo缓存/HOC高阶组件/Context上下文_第9张图片

ps:
因为 onPress 只能接受无参的函数,无法直接调用,所以可以将函数改造成一个高阶函数,这样就返回了一个无参的函数。

    const onItemPress = useCallback((item: any, index: any) => {
        return () => {
            console.log(`${item + index}`);
        }
    }, [])

三、高阶组件&高阶函数

高阶组件解决什么问题?

使用 HOC高阶组件解决横切关注点问题,使得组件功能更加单一,组件逻辑服务组件UI,从而降低耦合性,增强组件的复用性。

横切点关注点问题:
对于一个现有的组件而言,希望能在这个组件的某一个横切点去做一些事情,比如多渲染一个额外的视图,或者在组件的生命周期的某个节点去做一些事情,但是这个事情本身是和原始组件没有关系的。

什么是高阶函数?

如果一个函数接受的参数为函数,或者返回值是一个新函数,则该函数就是高阶函数。

setTimeout(() => {},1000);
array.filter((item, index) => item === target);
Promise...

什么是高阶组件?

如果一个组件的参数是组件,返回值是一个新组件,则该组件就是高阶组件。

高阶组件应用场景一(解耦):

如图所示,此处实现了一个添加按钮在页面右下角

React Native Ref转发/Memo缓存/HOC高阶组件/Context上下文_第10张图片

1、如果不使用高阶组件能不能达到同样的效果?

答案是能,只需要在详情页面加一个按钮也能达到同样的效果

缺点:

  • 所有的样式效果,功能模块都集中在一个页面里面了,即使有很多功能是与此页面的职责毫不相关,比如:页面中的添加按钮只是一个入口,与详情页是不相关的,它可以是在任何页面中显示,只是现在我们把它放到了详情页。这就是一种耦合,在详情页中添加这样一些与职责不相干的代码就是不合适的。
  • 假设有很多个页面都需要这样的添加按钮,就需要写很多重复代码在不同的页面。

2、使用高阶组件就能有效解决这些问题。

将特定的样式,逻辑,全都写在高阶组件中,使用时只需要包裹一下需要使用高阶组件实现的特定的功能的组件,就能将高阶组件特定的功能赋予被包裹的组件。

而且通过简单的包裹就能有效的提高代码复用率。

// 高阶组件函数,接受一个泛型参数 T,表示组件类型,以及一个字符串参数 type
export default <T extends IReactComponent>(
  OriginView: T | React.FC,  // OriginView 参数可以是实现了 IReactComponent 接口的组件或函数式组件
  type: string,  // type 参数是一个字符串
): T => {
  // 定义高阶组件内部的函数组件 HOCView
  const HOCView = (props: any) => {
    useEffect(() => {
      // 空的 useEffect 钩子,可用于添加其他副作用逻辑
    }, []);

    return (
      <>
        {/* 渲染传入的原始组件,通过 {...props} 将所有 props 传递给原始组件 */}
        <OriginView {...props} />
        
        {/* 添加一个 TouchableOpacity 组件,当点击时执行 console.log 输出信息 */}
        <TouchableOpacity
          style={styles.addButton}
          onPress={() => {
            console.log('onPress ...', type);
          }}>
          <Image style={styles.addImg} source={icon_add} />
        </TouchableOpacity>
      </>
    );
  };

  // 将 HOCView 转换为指定的泛型类型 T,并返回
  return HOCView as T;
};

React Native Ref转发/Memo缓存/HOC高阶组件/Context上下文_第11张图片

高阶组件使用场景二:

假设有一个需求:实现首页设备信息上报。(类似还有页面弹窗、申请系统权限等)

很简单的实现是:直接在首页页面的生命周期中发送请求,提交设备信息。

这里需要思考的是,上报设备信息这件事与首页的职能有关系吗?其实是没有关系的,只是这件事情刚好在首页发生了,在其他任何的页面上报设备信息都行,就这件事而言无论发生在哪里都是没有任何区别的。

从这个角度出发,把上报的代码直接写在首页就是不太合适的,这就出现了代码耦合,需要其解耦出去。

此处解耦的方式可以使用 hook 实现,也可以使用高阶组件实现。

高阶组件本质也是组件所以也有它自己的生命周期。

React Native Ref转发/Memo缓存/HOC高阶组件/Context上下文_第12张图片

注意事项

1、不要改变传入的原始组件的原型

需要做的是在原始组件的基础上额外附加一些东西,不要对原始组件做任何改动

React Native Ref转发/Memo缓存/HOC高阶组件/Context上下文_第13张图片

2、必要的话也是可以传入多个参数

React Native Ref转发/Memo缓存/HOC高阶组件/Context上下文_第14张图片

四、Context 上下文

Context解决什么问题?

在一个典型的反应应用中,数据是通过 props 属性逐层传递,这种做法对于某些数据而言是极其繁琐的 (如:登陆信息,用户界面主题) ,这些数据应用中许多组件都需要,而Context 上下文提供了一种在组件间共享值的方式,而不必显式地通过组件树逐层传递。

这样就可以对一些全局数据进行直接共享

React Native Ref转发/Memo缓存/HOC高阶组件/Context上下文_第15张图片
使用流程:

1、创建了一个名为ThemeContext的Context对象,用于在React应用程序中共享主题(theme)相关的数据,数据类型为字符串,并且默认值为’dark’。

import {createContext} from 'react';
export const ThemeContext = createContext<string>('dark');

2、使用包裹根节点,这样组件树中的任何组件都可以通过Context访问backgroundStyle这个值。

React Native Ref转发/Memo缓存/HOC高阶组件/Context上下文_第16张图片

Provider 这个命名惯例通常是为了表示在React中充当数据提供者的组件。

这是一种常见的命名模式,在React中,Context API中的数据提供者通常被称为 ,其中 SomeContext 是你创建的Context对象的名称。

这个命名约定有几个好处:

  1. 明确的角色:使用 Provider 作为后缀,清晰地表明了组件的作用是提供数据给内部和子组件,有助于理解组件的用途。

  2. 语义化:可以根据组件的名称推断出它的作用,语义化的命名有助于代码的可维护性和可读性。

  3. 一致性:这种命名约定是React社区的共识,有助于减少混淆和提高协作的效率。

总之,Provider 命名约定是React中的一种通用惯例,用于标识充当数据提供者的组件,有助于更清晰地组织和理解React应用程序的组件结构。

3、在应用的任何一个页面,都能使用 useContext 取到值

import React, {useContext} from 'react';
import {ThemeContext} from 'src/component/ThemeContext';
const HomeScreen: React.FC = () => {
  const theme = useContext(ThemeContext);
  return (<View></View>)
}

注意事项

  • 因为Context本质上就是全局变量,大量使用Context会导致组件失去独立性,使组件复用性变差。
  • 对于常规的组件间传值先考虑组件组合、状态管理、单例导出等方式,不要过度使用Context。

你可能感兴趣的:(React,Native,React,react,native,react.js)