React中useCallback的使用

目录

[🧠 核心逻辑解析](#🧠 核心逻辑解析)

[1. 数据驱动](#1. 数据驱动)

[2. genComponent 函数的设计](#2. genComponent 函数的设计)

[3. nanoid 的作用](#3. nanoid 的作用)

[4. Redux 流程](#4. Redux 流程)


代码示例

javascript 复制代码
// 导入 React 和相关 Hooks
import React, { FC, useCallback } from 'react'

// 用于生成唯一 ID 的库
import { nanoid } from 'nanoid'

// Ant Design 的 Typography 组件,用于展示标题
import { Typography } from 'antd'

// Redux 相关:用于派发 action
import { useDispatch } from 'react-redux'

// 导入所有问卷组件的配置定义
// componentConfGroup 是一个包含了所有组件分组和配置的数组
// ComponentConfType 定义了单个组件配置的 TypeScript 类型
import { componentConfGroup, ComponentConfType } from '../../../components/QuestionComponents'

// 导入 Redux action,用于向 store 中添加一个新的组件
import { addComponent } from '../../../store/componentsReducer'

// 导入 CSS Module 样式文件
import styles from './ComponentLib.module.scss'

// 解构 Ant Design 的 Title 组件,方便使用
const { Title } = Typography

/**
 * 生成单个组件元素的函数
 * 
 * 这是一个独立的函数,用于为每个组件配置项生成一个可点击的 React 元素。
 * 注意:它接收一个组件配置对象 `c` 作为参数。
 */
function genComponent(c: ComponentConfType) {
  // 从配置对象中解构出必要的属性
  const { title, type, Component, defaultProps } = c
  
  // 获取 Redux 的 dispatch 函数,用于触发状态更新
  const dispatch = useDispatch()

  // 使用 useCallback 优化点击处理函数
  // 依赖项为空数组 [],意味着这个函数在整个组件生命周期中只创建一次
  // 这样可以避免 genComponent 每次被调用时都创建一个新的函数引用,有助于性能优化
  const handleClick = useCallback(() => {
    // 当用户点击该组件时,派发一个 'addComponent' action
    dispatch(
      addComponent({
        fe_id: nanoid(),     // 使用 nanoid 生成一个唯一的前端 ID
        title,                // 组件标题,如 "单选题"
        type,                 // 组件类型,如 "questionRadio"
        props: defaultProps,   // 组件的默认属性,如 placeholder, title 等
      })
    )
  }, []) // 依赖数组为空,确保函数引用稳定

  // 返回一个 JSX 元素,代表侧边栏中的一个组件图标
  return (
    <div 
      key={type} // React 列表 key,这里 type 是唯一的
      className={styles.wrapper} // 外层容器样式,通常包含边框、内边距等
      onClick={handleClick} // 绑定点击事件
    >
      <div className={styles.component}>
        {/* 
          这里渲染的是组件的"预览图"。
          注意:Component 是一个 React 组件函数/类,这里通过 <Component /> 的方式执行它。
          通常在侧边栏中,这些组件会处于"预览模式",展示其基本形态但不可交互。
        */}
        <Component />
      </div>
    </div>
  )
}

/**
 * 组件库主组件 (SideBar)
 * 
 * 这是侧边栏的入口组件,负责组织和展示所有的组件分组。
 */
const Lib: FC = () => {
  return (
    <>
      {/* 
        遍历 componentConfGroup 数组。
        componentConfGroup 通常是这样结构的数据:
        [
          { groupId: '1', groupName: '通用组件', components: [...] },
          { groupId: '2', groupName: '高级组件', components: [...] }
        ]
      */}
      {componentConfGroup.map((group, index) => {
        // 解构出分组 ID、分组名称和该分组下的组件列表
        const { groupId, groupName, components } = group

        return (
          <div key={groupId}>
            {/* 渲染分组标题 */}
            <Title 
              level={3} 
              style={{ 
                fontSize: '16px', 
                // 如果不是第一个分组 (index > 0),则添加上边距,与上一个分组拉开距离
                marginTop: index > 0 ? '20px' : '0' 
              }}
            >
              {groupName}
            </Title>
            
            {/* 渲染该分组下的所有组件 */}
            <div>
              {/* 调用 genComponent 函数,为每个组件生成一个可点击的元素 */}
              {components.map(c => genComponent(c))}
            </div>
          </div>
        )
      })}
    </>
  )
}

expo

🧠 核心逻辑解析

1. 数据驱动
  • 数据源: 代码的核心是 componentConfGroup。这是一个配置文件,里面定义了所有可用的组件(如 Input, Radio, Checkbox)。
  • 结构: 它通常是一个数组,里面包含分组信息,每个分组里又包含具体的组件配置对象。
2. genComponent 函数的设计
  • 目的: 将数据(配置)转化为视图(UI)和行为(逻辑)。
  • 闭包: handleClick 函数被定义在 genComponent 内部,它"记住"了当前循环到的组件配置 c(通过解构赋值)。当你点击"单选题"时,它就知道要添加一个单选题的实例。
3. nanoid 的作用
  • 每当用户点击添加组件,都会调用 nanoid() 生成一个新的 fe_id
  • 意义: 这保证了画布上可以存在多个相同的组件(比如两个单选题),但它们拥有不同的 ID,从而可以拥有不同的属性(比如不同的标题、不同的选项)。
4. Redux 流程
  • 触发: 点击组件。
  • 动作: dispatch(addComponent(...))
  • 结果: Redux Store 中的 componentsReducer 会接收到这个动作,将新的组件配置添加到当前问卷的组件列表数组中。一旦 Store 更新,订阅了该状态的"画布"组件就会重新渲染,显示出新添加的题目。
相关推荐
咬人喵喵2 小时前
JavaScript 变量:let 和 const 该用谁?
前端·css·编辑器·交互·svg
麦麦大数据2 小时前
F059 vue+flask酒店对比系统
前端·vue.js·flask·携程·酒店对比·飞猪·同程
开发者小天2 小时前
React中的useState传入函数的好处
前端·javascript·react.js
Violet_YSWY2 小时前
Vue import.meta.env 讲解
前端·javascript·vue.js
snow@li2 小时前
小程序-uniapp:vue3-typescript项目使用mp-html实现展示富文本
javascript·typescript·uni-app
暴富暴富暴富啦啦啦2 小时前
实现自定义指令 v-scrollBar,用于动态显示/隐藏滚动条,提升用户体验
前端·javascript·vue.js
_Kayo_2 小时前
css 练习笔记1
前端·css·笔记
weixin_440730502 小时前
css的选择器
前端·css·css3
消失的旧时光-19432 小时前
从前端路由到 Android ARouter:观察者模式在不同平台的同一种落地
android·前端·观察者模式·flutter