react 常用hooks封装--useReactive

news2024/11/17 7:51:13

概述

一种具备响应式的useState

我们知道用useState可以定义变量格式为:

const [count, setCount] = useState(0)

通过 setCount 来进行设置,count 来获取,使用这种方式才能够渲染视图

来看看正常的操作,像这样 let count = 0; count = 7;此时count的值就是7,也就是说数据是响应式的。

那么我们可不可以将 useState 也写成响应式的呢?我们可以自由设置count的值,并且可以随时获取到count的最新值,而不是通过setCount来设置

那么,我们来想想怎么去实现一个具备响应式特点的useState也就是useRactive

提出以下疑问:

  • 这个钩子的出入参如何设定
  • 如何将数据制作成响应式--毕竟普通的操作无法刷新视图
  • 如何优化

分析

以上提出的疑问,最关键的就是第二个,我们如何将数据做成响应式的,要想做成响应式,就必须监听到值的变化,再作出更改也就是说,我们对这个数进行操作的时候,要进行相应的拦截,这里就可以用到es6的:Proxy

在这里会用到Proxy和Reflect

Proxy接受的参数是对象,所以第一个问题也就解决了,入参就为对象。那么如何去刷新视图?这里就需要用到另一个自定义的hooks--useUpdate来强制刷新,使数据更改

至于优化这块,这里会用到另一个自定hooks--useCreation,再配合useRef来放置initalState即可

useUpdate

有的时候我们需要组件强制更新,这个时候就可以使用这个钩子:

hooks/useUpdate.js

// 强制更新
import { useCallback, useState } from "react";

const useUpdate = () => {
    const [, setUpdate] = useState({});

    return useCallback(() => setUpdate({}), []);
}

export default useUpdate;

useCreation

这里封装的 useCreation 是 useMemo 或 useRef 的代替品,换言之useCreation这个钩子增强了useMemo 和 useRef ,让这个钩子可以替换这两个钩子。

  • useMemo的值不一定是最新的值,但useCreation可以保证拿到的值一定是最新的值
  • 对于复杂常量的创建,useRef容易出现潜在的的性能隐患,但useCreation可以避免

这里的性能隐患是指:

接下来我们来看看如何封装一个useCreation,首先我们要明白以下三点:

  • 第一点:先确定参数,useCreation 的参数与 useMemo 的一致,第一个参数是函数,第二个参数参数是可变的数组
  • 第二点:我们的值要保存在useRef中,这样可以将值缓存,从而减少无关的刷新
  • 第三点:更新值的判断,怎么通过第二个参数来判断是否更新 useRef 里的值。

明白了一上三点我们就可以自己实现一个 useCreation

hooks/useCreation.js

import { useRef } from 'react';

const depsAreSame = (oldDeps, deps) => {
    if(oldDeps == deps) return true;

    for(let i = 0; i < oldDeps.length; i++) {
        // 判断两个值是否是同一个值
        if(!Object.is(oldDeps[i], deps[i])) return false;
    }
    return true;
}

const useCreation = (fn, deps) => {
    const { current } = useRef({
        deps,
        obj: undefined,
        initialized: false,
    });

    if(current.initialized === false || !depsAreSame(current.deps, deps)) {
        current.deps = deps;
        current.obj = fn();
        current.initialized = true;
    }
    return current.obj;
}

export default useCreation;

验证:

useReactive

hooks/useReactive.js

import { useRef } from 'react';
import useUpdate from "./useUpdate.js";
import useCreation from "./useCreation.js";

const observer = (initialVal, cb) => {
    const proxy = new Proxy(initialVal, {
        get(target, key, receiver) {
            const res = Reflect.get(target, key, receiver);
            return typeof res === 'object' ? observer(res, cb) : Reflect.get(target, key);
        },
        set(target, key, val) {
            const ret = Reflect.set(target, key, val);
            cb();
            return ret;
        }
    })
    return proxy;
}


const useReactive = (initialState) => {
    const ref = useRef(initialState);
    const update = useUpdate();

    const state = useCreation(() => {
        return observer(ref.current, () => {
            update()
        });
    }, [])

    return state;
}

export default useReactive;

TS版(仅供参考):

使用

demo.jsx

import React, {  } from 'react';
import useReactive from "../../hooks/useReactive.js";

export const Home = () => {

    const state = useReactive({
        count: 0,
        name: '小度小度',
        flag: true,
        arr: [],
        bugs: ['小度', 'react', 'hook'],
        addBug(bug) {
            this.bugs.push(bug)
        },
        get bugsCount() {
            return this.bugs.length;
        },
    });

  return (
      <div className="App" style={{padding: 20}}>
          <div style={{fontWeight: 'bold'}}>基本使用</div>
          <div style={{marginTop: 8}}>对数字进行操作:{state.count}</div>
          <div style={{margin: '8px 0', display: 'flex', justifyContent: 'flex-start'}}>
              <button onClick={() => state.count++}>加1</button>
              <button style={{marginLeft: 8}} onClick={() => state.count--}>减1</button>
              <button style={{marginLeft: 8}} onClick={() => state.count = 7}>设置为7</button>
          </div>
          <div style={{marginTop: 8}}>对字符串进行操作:{state.name}</div>
          <div style={{margin: '8px 0', display: 'flex', justifyContent: 'flex-start'}}>
              <button onClick={() => state.name = '小嘟嘟'}>设置为小嘟嘟</button>
              <button style={{marginLeft: 8}} onClick={() => state.name = 'Demesy'}>设置为Domesy</button>
          </div>
          <div style={{marginLeft: 8}}>对布尔值进行操作:{JSON.stringify(state.flag)}</div>
          <div style={{margin: '8px 0', display: 'flex', justifyContent: 'flex-start'}}>
              <button onClick={() => state.flag = !state.flag}>切换状态</button>
          </div>
          <div style={{marginLeft: 8}}>对数组进行操作:{JSON.stringify(state.arr)}</div>
          <div style={{margin: '8px 0', display: 'flex', justifyContent: 'flex-start'}}>
              <button onClick={() => state.arr.push(Math.floor(Math.random() * 100))}>push</button>
              <button style={{marginLeft: 8}} onClick={() => state.arr.pop()}>pop</button>
              <button style={{marginLeft: 8}} onClick={() => state.arr.shift()}>shift</button>
              <button style={{marginLeft: 8}} onClick={() => state.arr.unshift(Math.floor(Math.random() * 100))}>unshift</button>
              <button style={{marginLeft: 8}} onClick={() => state.arr.reverse()}>reverse</button>
              <button style={{marginLeft: 8}} onClick={() => state.arr.sort()}>sort</button>
          </div>
          <div style={{fontWeight: 'bold', marginTop: 8}}>计算属性:</div>
          <div style={{marginTop: 8}}>数量:{state.bugsCount}个</div>
          <div style={{margin: '8px 0'}}>
              <form onSubmit={(e) => {
                  state.bug ? state.addBug(state.bug) : state.addBug('domesy')
                  state.bug = ''
                  e.preventDefault()
              }}>
                  <input type="text" value={state.bug} onChange={(e) => state.bug = e.target.value}/>
                  <button type="submit" style={{marginLeft: 8}}>增加</button>
                  <button style={{marginLeft: 8}} onClick={() => state.bug.pop()}>删除</button>
              </form>
          </div>
          <ul>
              {
                  state.bugs.map((bug, index) => {
                      <li key={index}>{bug}</li>
                  })
              }
          </ul>
      </div>
  );
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2173330.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

open-resty 服务安装redis插件

从github下载 作者&#xff1a;程序那点事儿 日期&#xff1a;2023/11/16 22:04 lua-resty-redis-cluster cd /usr/local/openresty/modules #进入到modules目录git clone https://github.com/cuiweixie/lua-resty-redis-cluster.git #下载插件mv lua-resty-redis-cluster/ …

数据结构编程实践20讲(Python版)—03栈

本文目录 03 栈 StackS1 说明S2 示例基于列表的实现基于链表的实现 S3 问题&#xff1a;复杂嵌套结构的括号匹配问题求解思路Python3程序 S4 问题&#xff1a;基于栈的阶乘计算VS递归实现求解思路Python3程序 S5 问题&#xff1a;逆波兰表示法(后缀表达式)求值求解思路Python3程…

unix中父进程如何获取子进程的终止状态

一、前言 本文将介绍在unix系统中&#xff0c;父进程如何获取子进程的终止状态。本文主要围绕如下函数展开&#xff1a; 1.wait 2.waitpid 3.waitid 4.wait3、wait4 在讨论这些函数前&#xff0c;先介绍一个进程从创建到释放子进程的过程。 二、子进程的创建以及终止 在unix…

【Java】单元测试【主线学习笔记】

文章目录 前言测试分类JUnit单元测试介绍编写单元测试方法的条件IDEA中简易使用JUnit 前言 Java是一门功能强大且广泛应用的编程语言&#xff0c;具有跨平台性和高效的执行速度&#xff0c;广受开发者喜爱。在接下来的学习过程中&#xff0c;我将记录学习过程中的基础语法、框架…

大联大友尚集团推出基于炬芯科技产品的蓝牙音箱方案

大联大控股宣布&#xff0c;其旗下友尚推出基于炬芯科技&#xff08;Actions&#xff09;ATS2835P蓝牙音频SoC的蓝牙音箱方案。 图示1-大联大友尚基于炬芯科技产品的蓝牙音箱方案的展示板图 在智能音频设备市场持续升温的浪潮中&#xff0c;蓝牙音箱凭借音质卓越、操作简便等…

[Linux] Linux操作系统 进程的优先级 环境变量(一)

标题&#xff1a;[Linux] Linux操作系统 进程的优先级 个人主页水墨不写bug &#xff08;图片来源于网络&#xff09; 目录 一、进程优先级 1.PRI and NI 2.PRI vs NI 的补充理解 二、命令行参数和环境变量 正文开始&#xff1a; 一、进程优先级 基本概念 进程的调用需要CP…

Study--Oracle-09--部署Openfiler存储服务器

免费的存储服务器软件有FreeNAS 和 Openfiler。 其中Freenas的网站上只有i386及amd64的版本&#xff0c;也就是说Freenas不能支持64位版本的Intel CPU&#xff0c;而Openfiler则提供更全面的版本支持&#xff0c;在其网站上可以看到支持多网卡、多CPU&#xff0c;以及硬件Raid的…

manim中获取并定位不规则页面的中心位置

介绍中心点 找到中心点非常重要&#xff0c;因为它在多个领域中都有重要的应用和意义。中心点可以指一个几何形状的中心、数据集的平均值中心、或是特定情境下的关键焦点。以下是一些中心点重要性的具体解释&#xff1a; 数学与几何&#xff1a;在几何中&#xff0c;中心点&…

jinaai/jina-embeddings-v2-base-zh向量模型报错解决

报错信息 OSError: We couldn’t connect to ‘https://huggingface.co’ to load this file, couldn’t find it in the cached files and it looks like jinaai/jina-bert-implementation is not the path to a directory containing a file named configuration_bert.py. 报…

C++ | Leetcode C++题解之第441题排列硬币

题目&#xff1a; 题解&#xff1a; class Solution { public:int arrangeCoins(int n) {return (int) ((sqrt((long long) 8 * n 1) - 1) / 2);} };

探索Python新境界:funboost库揭秘

文章目录 探索Python新境界&#xff1a;funboost库揭秘背景&#xff1a;为什么选择funboost&#xff1f;funboost是什么&#xff1f;如何安装funboost&#xff1f;简单的库函数使用方法场景应用常见Bug及解决方案总结 探索Python新境界&#xff1a;funboost库揭秘 背景&#x…

什么是前缀索引?

什么是前缀索引&#xff1f; 1、什么是前缀索引&#xff1f;2、为什么要使用前缀索引&#xff1f;3、如何选择前缀长度&#xff1f;4、创建前缀索引的SQL语法5、示例 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在处理包含长字符串的数据…

yolo自动化项目实例解析(七)自建UI--工具栏选项

在上一章我们基本实现了关于预览窗口的显示&#xff0c;现在我们主要完善一下工具栏菜单按键 一、添加工具栏ui 1、配置文件读取 我们后面要改的东西越来越多了&#xff0c;先加个变量文件方便我们后面调用 下面我们使用的config.get意思是从./datas/setting.ini文件中读取关键…

RedisBoost Web缓存加速平台

1.产品介绍 产品名称:RedisBoost Web缓存加速平台 主要功能: 智能缓存策略配置 功能描述:RedisBoost提供了一套直观易用的缓存策略配置界面,允许用户根据业务场景自定义缓存策略,包括缓存时间(TTL)、缓存淘汰算法(如LRU、LFU)、数据分区与分片策略等。支持动态调整策…

SSM私人诊所管理系统—计算机毕业设计源码36406

摘 要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#xff0c;科学化的管理&#xff0c;使信息存…

Redis高级特性及应用

一、Redis慢查询 1.1 Redis命令流程 1.2 慢查询配置&#xff1a; 可以通过以下命令配置慢查询时间阈值&#xff08;慢查询值得是上图中执行命令的时间&#xff0c;不包含其他时间&#xff09; config set slowlog-log-slower-than 10000 //单位微秒 config rewrite //写入…

【设计模式-访问者模式】

定义 访问者模式&#xff08;Visitor Pattern&#xff09;是一种行为型设计模式&#xff0c;允许你在不修改已有类的情况下向这些类添加新的功能或行为。它通过将操作的执行逻辑从对象的类中分离出来&#xff0c;使得你可以在保持类的封闭性&#xff08;符合开闭原则&#xff…

Leetcode 740. 删除并获得点数

原题链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 给你一个整数数组 nums &#xff0c;你可以对它进行一些操作。 每次操作中&#xff0c;选择任意一个 nums[i] &#xff0c;删除它并获得 nums[i] 的点数。之后&#xff0c;你必须删除 所有 等于 nums[i] - 1 和…

LeetCode题练习与总结:搜索二维矩阵 Ⅱ--240

一、题目描述 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性&#xff1a; 每行的元素从左到右升序排列。每列的元素从上到下升序排列。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],…

详细解析msyql和消息队列数据一致性问题

目录 前言 保持系统数据同步&#xff08;双写问题&#xff09; 消息队列消息丢失的问题 总结 前言 在当今互联网飞速发展的时代&#xff0c;随着业务复杂性的不断增加&#xff0c;消息队列作为一种重要的技术手段&#xff0c;越来越多地被应用于各种场景。它们不仅能有效解…