沐游虞笔记
  • 前端面试题

    • HTML面试题汇总(无答案)
    • HTML面试题汇总
    • CSS 面试题汇总(无答案)
    • CSS 面试题汇总
    • javascript 面试题汇总(无答案)
    • javascript 面试题汇总
    • promise 面试题(无答案)
    • promise 面试题
    • 浏览器面试题汇总(无答案)
    • 浏览器面试题汇总
    • 网络面试题汇总(无答案)
    • 网络面试题汇总
    • 工程化面试题汇总(无答案)
    • 工程化面试题汇总
    • VUE面试题汇总(无答案)
    • VUE面试题汇总
  • 直播课文件

    • 静态页面学习指导
    • 属性的计算过程
    • 层叠继承规则总结
    • BFC
    • JS基础知识回顾
    • DOM 事件的传播机制
    • DOM 事件的注册和移除
    • 阻止事件默认行为
    • 基础领航考试题
    • 基础领航考试题(答案)
    • 2024前端发展
    • JS核心概念学习指导
    • 第三方库与工程化学习指导
    • Vue入门学习指导
    • vue进阶学习指导
    • 前端性能优化
  • 笔面试环节知识讲解

    • 目录
    • 图像处理
    • 图像处理(面试)
    • Webpack构建优化
    • Webpack构建优化(面试)
    • TTS性能优化
    • TTS性能优化(面试)
    • 实时协作
    • 实时协作(面试)
    • 网页复制图片到剪贴板
    • 网页复制图片到剪贴板(面试)
    • vite插件
    • vite插件(面试)
    • 表单数据同步与保持
    • 表单数据同步与保持(面试)
    • 优化虚拟列表
    • 优化虚拟列表(面试)
    • 微前端解决巨石应用
    • 微前端解决巨石应用(面试)
    • DNS解析与优化
    • DNS解析与优化(面试)
    • 前端监控
    • 前端监控(面试)
    • 12.跨标签页通信
    • 12.跨标签页通信(面试)
    • 13.Vite相关优化
    • 13.Vite相关优化(面试)
    • 14.计时器节流问题
    • 14.计时器节流问题(面试)
    • 15.多文件预览支持
    • 15.多文件预览支持(面试)
    • 16.defer优化白屏时间
    • 16.defer优化白屏时间(面试)
  • Vue3整体变化
  • Vue2响应式回顾
  • Vue3响应式变化
  • nextTick实现原理
  • 两道代码题
  • Vue运行机制
  • 渲染器核心功能
  • 事件绑定与更新
  • computed面试题
  • watch面试题
  • 图解双端diff
  • 图解快速dff
  • 最长递增子序列
  • 模板编译器
  • 模板编译提升
  • 组件name作用
  • 路由传参方式
  • 基础篇

    • 序章React介绍
    • JSX基础语法
    • React基本介绍
    • 表单
    • 生命周期
    • 组件与事件绑定
    • 组件状态与数据传递
    • Hooks
    • React--redux介绍
    • React-router介绍
  • 就业篇

    • 属性默认值和类型验证
    • 高阶组件
    • Ref
    • Context
    • Render Props
    • Portals
    • 错误边界
    • 组件渲染性能优化
    • 前端框架的理解
    • Reacti和Vue描述页面的区别
    • 前端框架的分类
    • 虚拟DoM
    • React整体架构
    • React渲染流程
    • Fiber双缓冲
    • MessageChannel
    • Scheduleri调度普通任务
    • Scheduleri调度延时任务
    • 最小堆
    • React中的位运算
    • beginWork工作流程
    • completeWork工作流程
    • 图解diff算法
    • commit工作流程
    • lane模型
    • React中的事件
    • Hooks原理
    • useStateuseReducer.
    • effect相关hook
    • useCallbackuseMemo
    • useRef
    • Update
    • 性能优化策略之eagerState
    • 性能优化策略之bailout
    • bailoutContextAPl
    • 性能优化对日常开发启示
  • 前端监控概述
  • 错误监控
  • 数据上报
  • 页面性能监控
  • 用户行为收集与埋点
  • CSS3手册
  • HTML5手册
  • JavaScript语言提升

    • es补充
    • 事件循环
    • promise基础
    • Promise的链式调用
    • Promise的静态方法
    • async和await
    • Promise相关面试题
  • 网络

    • 客户端与服务器
    • 关于 Apifox 的使用
  • git文档
  • 工程化

    • CommonJS
    • ES module
    • npm文档(包管理)
    • Lass笔记
    • webpack工具
  • canvas详解
  • uinapp笔记
  • 自动化测试
  • oauth2令牌

    • 认识Oauth2
    • 三方应用实现github授权
    • 微信三方应用登录实现
    • 支付宝沙箱支付功能
  • 前端面试题

    • HTML面试题汇总(无答案)
    • HTML面试题汇总
    • CSS 面试题汇总(无答案)
    • CSS 面试题汇总
    • javascript 面试题汇总(无答案)
    • javascript 面试题汇总
    • promise 面试题(无答案)
    • promise 面试题
    • 浏览器面试题汇总(无答案)
    • 浏览器面试题汇总
    • 网络面试题汇总(无答案)
    • 网络面试题汇总
    • 工程化面试题汇总(无答案)
    • 工程化面试题汇总
    • VUE面试题汇总(无答案)
    • VUE面试题汇总
  • 直播课文件

    • 静态页面学习指导
    • 属性的计算过程
    • 层叠继承规则总结
    • BFC
    • JS基础知识回顾
    • DOM 事件的传播机制
    • DOM 事件的注册和移除
    • 阻止事件默认行为
    • 基础领航考试题
    • 基础领航考试题(答案)
    • 2024前端发展
    • JS核心概念学习指导
    • 第三方库与工程化学习指导
    • Vue入门学习指导
    • vue进阶学习指导
    • 前端性能优化
  • 笔面试环节知识讲解

    • 目录
    • 图像处理
    • 图像处理(面试)
    • Webpack构建优化
    • Webpack构建优化(面试)
    • TTS性能优化
    • TTS性能优化(面试)
    • 实时协作
    • 实时协作(面试)
    • 网页复制图片到剪贴板
    • 网页复制图片到剪贴板(面试)
    • vite插件
    • vite插件(面试)
    • 表单数据同步与保持
    • 表单数据同步与保持(面试)
    • 优化虚拟列表
    • 优化虚拟列表(面试)
    • 微前端解决巨石应用
    • 微前端解决巨石应用(面试)
    • DNS解析与优化
    • DNS解析与优化(面试)
    • 前端监控
    • 前端监控(面试)
    • 12.跨标签页通信
    • 12.跨标签页通信(面试)
    • 13.Vite相关优化
    • 13.Vite相关优化(面试)
    • 14.计时器节流问题
    • 14.计时器节流问题(面试)
    • 15.多文件预览支持
    • 15.多文件预览支持(面试)
    • 16.defer优化白屏时间
    • 16.defer优化白屏时间(面试)
  • Vue3整体变化
  • Vue2响应式回顾
  • Vue3响应式变化
  • nextTick实现原理
  • 两道代码题
  • Vue运行机制
  • 渲染器核心功能
  • 事件绑定与更新
  • computed面试题
  • watch面试题
  • 图解双端diff
  • 图解快速dff
  • 最长递增子序列
  • 模板编译器
  • 模板编译提升
  • 组件name作用
  • 路由传参方式
  • 基础篇

    • 序章React介绍
    • JSX基础语法
    • React基本介绍
    • 表单
    • 生命周期
    • 组件与事件绑定
    • 组件状态与数据传递
    • Hooks
    • React--redux介绍
    • React-router介绍
  • 就业篇

    • 属性默认值和类型验证
    • 高阶组件
    • Ref
    • Context
    • Render Props
    • Portals
    • 错误边界
    • 组件渲染性能优化
    • 前端框架的理解
    • Reacti和Vue描述页面的区别
    • 前端框架的分类
    • 虚拟DoM
    • React整体架构
    • React渲染流程
    • Fiber双缓冲
    • MessageChannel
    • Scheduleri调度普通任务
    • Scheduleri调度延时任务
    • 最小堆
    • React中的位运算
    • beginWork工作流程
    • completeWork工作流程
    • 图解diff算法
    • commit工作流程
    • lane模型
    • React中的事件
    • Hooks原理
    • useStateuseReducer.
    • effect相关hook
    • useCallbackuseMemo
    • useRef
    • Update
    • 性能优化策略之eagerState
    • 性能优化策略之bailout
    • bailoutContextAPl
    • 性能优化对日常开发启示
  • 前端监控概述
  • 错误监控
  • 数据上报
  • 页面性能监控
  • 用户行为收集与埋点
  • CSS3手册
  • HTML5手册
  • JavaScript语言提升

    • es补充
    • 事件循环
    • promise基础
    • Promise的链式调用
    • Promise的静态方法
    • async和await
    • Promise相关面试题
  • 网络

    • 客户端与服务器
    • 关于 Apifox 的使用
  • git文档
  • 工程化

    • CommonJS
    • ES module
    • npm文档(包管理)
    • Lass笔记
    • webpack工具
  • canvas详解
  • uinapp笔记
  • 自动化测试
  • oauth2令牌

    • 认识Oauth2
    • 三方应用实现github授权
    • 微信三方应用登录实现
    • 支付宝沙箱支付功能
  • 基础篇

    • 序章:React课程介绍
    • JSX 基础语法
    • React 基本介绍
    • 表单
    • 生命周期
    • 组件与事件绑定
    • 组件状态与数据传递
    • Hooks
    • React-redux 介绍
    • React-router介绍
  • 就业篇

    • 属性默认值和类型验证
    • 高阶组件
    • Ref
    • Context
    • Render Props
      • 如何使用 Render Props
      • 何时使用 Render Props
    • Portals
    • 错误边界
    • 组件渲染性能优化
    • 前端框架的理解
    • React和Vue描述页面的区别
    • 前端框架的分类
    • 虚拟 DOM
    • React 整体架构
    • React 渲染流程
    • Fiber双缓冲
    • MessageChannel
    • Scheduler调度普通任务
    • Scheduler调度延时任务
    • 最小堆
    • React中的位运算
    • beginWork工作流程
    • completeWork工作流程
    • 图解diff算法
    • commit 工作流程
    • lane模型
    • React 中的事件
    • Hooks原理
    • useState和useReducer
    • effect相关hook
    • useCallback和useMemo
    • useRef
    • Update
    • 性能优化策略之eagerState
    • 性能优化策略之bailout
    • bailout和ContextAPI
    • 性能优化对日常开发启示
  • react
  • 就业篇
luzhichang
2024-10-17
目录

Render Props

# 5. Render Props

在 React 中,代码复用的最基本单位就是组件,但是如果组件中也出现了重复的代码,该怎么做呢?

那么我们需要通过某种方式将代码中公共的部分抽取出来,这些公共的部分就被称之为横切关注点(Cross-Cutting Concerns)

在 React 中,常见有两种方式来进行横切关注点的抽离:

  • 高阶组件(HOC)
  • Render Props

Render Props 实际上**本身并非什么新语法,而是指一种在 React 组件之间使用一个值为函数的 prop 共享代码**的简单技术。

有关 Render Props,咱们主要需要掌握以下 2 个点:

  • 如何用?
  • 何时用?

# 如何使用 Render Props

我们首先还是来看一个示例:

// App.jsx
import ChildCom1 from "./components/ChildCom1"
import ChildCom2 from "./components/ChildCom2"

function App() {
  return (
    <div style={{
      display: 'flex',
      justifyContent: 'space-between',
      width: "850px"
    }}>
      <ChildCom1 />
      <ChildCom2 />
    </div>
  );
}

export default App;
// components/ChildCom1.jsx
import { useState } from 'react';

function ChildCom1() {

    const [points, setPoints] = useState({
        x: 0,
        y: 0
    })

    function handleMouseMove(e) {
        setPoints({
            x: e.clientX,
            y: e.clientY
        })
    }

    return (
        <div style={{
            width: '400px',
            height: '400px',
            backgroundColor: 'red'
        }} onMouseMove={handleMouseMove}>
            <h1>移动鼠标!</h1>
            <p>当前的鼠标位置是 ({points.x}, {points.y})</p>
        </div>
    );
}

export default ChildCom1;
// components/ChildCom2.jsx

import { useState } from 'react';

function ChildCom2() {

    const [points, setPoints] = useState({
        x: 0,
        y: 0
    })

    function handleMouseMove(e) {
        setPoints({
            x: e.clientX,
            y: e.clientY
        })
    }

    return (
        <div style={{
            width: '400px',
            height: '400px',
            backgroundColor: 'grey',
            position: 'relative',
            overflow: 'hidden'
        }} onMouseMove={handleMouseMove}>
            <h1>移动鼠标!</h1>
            {/* 这里减去 460 是因为要减去左边 div 的宽度 + 两个大 div 之间 50 的间距 */}
            <div style={{
                width: '15px',
                height: '15px',
                borderRadius: "50%",
                backgroundColor: 'white',
                position: 'absolute',
                left: points.x - 5 - 460,
                top: points.y - 5 - 10,
            }}></div>
        </div>
    );
}

export default ChildCom2;

在上面的代码中,App 根组件下渲染了两个子组件,这两个子组件一个是显示鼠标的位置,另外一个是根据鼠标位置显示一个追随鼠标移动的小球。

仔细观察代码,你会发现这两个子组件内部的逻辑基本上是一模一样的,只是最终渲染的内容不一样,此时我们就可以使用 Render Props 对横切关注点进行一个抽离。

方式也很简单,就是在一个组件中使用一个值为函数的 prop,函数的返回值为要渲染的视图。

// /components/MouseMove.jsx

import { useState } from 'react';

function MouseMove(props) {
    const [points, setPoints] = useState({
        x: 0,
        y: 0
    })

    function handleMouseMove(e) {
        setPoints({
            x: e.clientX,
            y: e.clientY
        })
    }

    return (
        props.render ? props.render({ points, handleMouseMove }) : null
    );
}

export default MouseMove;

在上面的代码中,我们新创建了一个 MouseMove 组件,该组件就封装了之前 ChildCom1 和 ChildCom2 组件的公共逻辑。该组件的 props 接收一个名为 render 的参数,只不过该参数对应的值为一个函数,我们调用时将对应的状态和处理函数传递过去,该函数的调用结果为返回一段视图。

import ChildCom1 from "./components/ChildCom1";
import ChildCom2 from "./components/ChildCom2";
import MouseMove from "./components/MouseMove";

function App() {
  return (
    <div style={{
      display: 'flex',
      justifyContent: 'space-between',
      width: "850px"
    }}>
      <MouseMove render={props => <ChildCom1 {...props} />} />
      <MouseMove render={props => <ChildCom2 {...props} />} />
    </div>
  );
}

export default App;

接下来在 App 根组件中,我们使用 MouseMove 组件,该组件上有一个 render 属性,对应的值就是函数,函数返回要渲染的组件。

function ChildCom1(props) {
    return (
        <div style={{
            width: '400px',
            height: '400px',
            backgroundColor: 'red'
        }} onMouseMove={props.handleMouseMove}>
            <h1>移动鼠标!</h1>
            <p>当前的鼠标位置是 ({props.points.x}, {props.points.y})</p>
        </div>
    );
}

export default ChildCom1;
function ChildCom2(props) {
    return (
        <div style={{
            width: '400px',
            height: '400px',
            backgroundColor: 'grey',
            position: 'relative',
            overflow: 'hidden'
        }} onMouseMove={props.handleMouseMove}>
            <h1>移动鼠标!</h1>
            {/* 这里减去 460 是因为要减去左边 div 的宽度 + 两个大 div 之间 50 的间距 */}
            <div style={{
                width: '15px',
                height: '15px',
                borderRadius: "50%",
                backgroundColor: 'white',
                position: 'absolute',
                left: props.points.x - 5 - 460,
                top: props.points.y - 5 - 10,
            }}></div>
        </div>
    );
}

export default ChildCom2;

最后就是子组件 ChildCom1 和 ChildCom2 的改写,可以看到这两个子组件就只需要书写要渲染的视图了。公共的逻辑已经被 MouseMove 抽取出去了。

另外需要说明的是,虽然这个技巧的名字叫做 Render Props,但不是说必须要提供一个名为 render 的属性,事实上,封装公共逻辑的组件(例如上面的 MouseMove)只要能够得到要渲染的视图即可,所以传递的方式可以有多种。

例如:

import ChildCom1 from "./components/ChildCom1";
import ChildCom2 from "./components/ChildCom2";
import MouseMove from "./components/MouseMove";

function App() {
  return (
    <div style={{
      display: 'flex',
      justifyContent: 'space-between',
      width: "850px"
    }}>
      <MouseMove>
        {(props) => <ChildCom1 {...props} />}
      </MouseMove>
      <MouseMove>
        {props => <ChildCom2 {...props} />}
      </MouseMove>
    </div>
  );
}

export default App;

上面使用 MouseMove 组件时,并没有传递什么 render 属性,而是通过 props.children 的形式将要渲染的视图传递到了组件内部。

在 MouseMove 组件内部,就不再是执行 render 方法了,而是应该执行 props.children,如下:

props.children ? props.children({ points, handleMouseMove }) : null

# 何时使用 Render Props

知道了 Render Props 的使用方法后,接下来我们来看一下何时使用 Render Props。

同样是作为抽离横切关注点,前面所讲的 HOC 也能做到相同的效果。例如我们可以将上面的示例修改为 HOC 的方式。

// HOC/withMouseMove.js

import { useState } from "react";
function withMouseMove(Com) {
  // 返回一个新组件
  return function NewCom() {
    const [points, setPoints] = useState({
      x: 0,
      y: 0,
    });
    function handleMouseMove(e) {
      setPoints({
        x: e.clientX,
        y: e.clientY,
      });
    }

    const mouseHandle = { points, handleMouseMove };
    return <Com {...mouseHandle} />;
  };
}

export default withMouseMove;
// App.jsx

import ChildCom1 from "./components/ChildCom1";
import ChildCom2 from "./components/ChildCom2";
import withMouseMove from "./HOC/withMouseMove"

const NewChildCom1 = withMouseMove(ChildCom1);
const NewChildCom2 = withMouseMove(ChildCom2);

function App() {
  return (
    <div style={{
      display: 'flex',
      justifyContent: 'space-between',
      width: "850px"
    }}>
      <NewChildCom1 />
      <NewChildCom2 />
    </div>
  );
}

export default App;

那么自然而然疑问就来了,什么时候使用 Render Props ?什么时候使用 HOC ?

一般来讲,Render Props 应用于组件之间功能逻辑完全相同,仅仅是渲染的视图不同。这个时候我们可以通过 Render Props 来指定要渲染的视图是什么。

而 HOC 一般是抽离部分公共逻辑,也就是说组件之间有一部分逻辑相同,但是各自也有自己独有的逻辑,那么这个时候使用 HOC 比较合适,可以在原有的组件的基础上做一个增强处理。


-EOF-

Context
Portals

← Context Portals→

Theme by Vdoing | Copyright © 2021-2024 蜀ICP备2024068710号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式