react usecontext_Vue3原理实战运用,我用40行代码把他装进了React做状态管理

前言

vue-next是Vue3的源码仓库,Vue3采用lerna做package的划分,而响应式能力@vue/reactivity被划分到了单独的一个package中。

如果我们想把它集成到React中,可行吗?来试一试吧。

使用示例

话不多说,先看看怎么用的解解馋吧。

// store.ts
import { reactive, computed, effect } from '@vue/reactivity';

export const state = reactive({
  count: 0,
});

const plusOne = computed(() => state.count + 1);

effect(() => {
  console.log('plusOne changed: ', plusOne);
});

const add = () => (state.count += 1);

export const mutations = {
  // mutation
  add,
};

export const store = {
  state,
  computed: {
    plusOne,
  },
};

export type Store = typeof store;
// Index.tsx
import { Provider, useStore } from 'rxv'
import { mutations, store, Store } from './store.ts'
function Count() {
  const countState = useStore((store: Store) => {
    const { state, computed } = store;
    const { count } = state;
    const { plusOne } = computed;

    return {
      count,
      plusOne,
    };
  });

  return (
    <Card hoverable style={{ marginBottom: 24 }}><h1>计数器h1><div className="chunk"><div className="chunk">store中的count现在是 {countState.count}div><div className="chunk">computed值中的plusOne现在是 {countState.plusOne.value}div><Button onClick={mutations.add}>addButton>div>Card>
  );
}

export default () => {
  return (
    <Provider value={store}><Count />Provider>
  );
};

可以看出,store的定义只用到了@vue/reactivity,而rxv只是在组件中做了一层桥接,连通了Vue3和React,然后我们就可以尽情的使用Vue3的响应式能力啦。

预览

844be8a7f14c824a68f00d7b6b463f7e.gif
gif

可以看到,完美的利用了reactive、computed的强大能力。

分析

从这个包提供的几个核心api来分析:

effect(重点)

effect其实是响应式库中一个通用的概念:观察函数,就像Vue2中的Watcher,mobx中的autorunobserver一样,它的作用是收集依赖

它接受的是一个函数,它会帮你执行这个函数,并且开启依赖收集,

这个函数内部对于响应式数据的访问都可以收集依赖,那么在响应式数据被修改后,就会触发更新。

最简单的用法

const data = reactive({ count: 0 })
effect(() => {
    // 就是这句话 访问了data.count
    // 从而收集到了依赖
    console.log(data.count)
})

data.count = 1
// 控制台打印出1

那么如果把这个简单例子中的

() => {
    // 就是这句话 访问了data.count
    // 从而收集到了依赖
    console.log(data.count)
}

这个函数,替换成React的组件渲染,是不是就能达成响应式更新组件的目的了?

reactive(重点)

响应式数据的核心api,这个api返回的是一个proxy,对上面所有属性的访问都会被劫持,从而在get的时候收集依赖(也就是正在运行的effect),在set的时候触发更新。

ref

对于简单数据类型比如number,我们不可能像这样去做:

let data = reactive(2)
// ?oops
data = 5

这是不符合响应式的拦截规则的,没有办法能拦截到data本身的改变,只能拦截到data身上的属性的改变,所以有了ref。

const data = ref(2)
// ?ok
data.value= 5

computed

计算属性,依赖值更新以后,它的值也会随之自动更新。其实computed内部也是一个effect。

拥有在computed中观察另一个computed数据、effect观察computed改变之类的高级特性。

实现

从这几个核心api来看,只要effect能接入到React系统中,那么其他的api都没什么问题,因为它们只是去收集effect的依赖,去通知effect触发更新。

effect接受的是一个函数,而且effect还支持通过传入schedule参数来自定义依赖更新的时候需要触发什么函数,如果我们把这个schedule替换成对应组件的更新呢?要知道在hook的世界中,实现当前组件强制更新可是很简单的:

useForceUpdate

export const useForceUpdate = () => {
  const [, forceUpdate] = useReducer(s => s + 1, 0);
  return forceUpdate;
};

这是一个很经典的自定义hook,通过不断的把状态+1来强行让组件渲染。

rxv的核心api: useStore接受的也是一个函数selector,它会让用户自己选择在组件中需要访问的数据。

那么思路就显而易见了:

  1. selector包装在effect中执行,去收集依赖。
  2. 指定依赖发生更新时,需要调用的函数是当前正在使用useStore的这个组件的forceUpdate强制渲染函数。

这样不就实现了数据变化,组件自动更新吗?

简单的看一下核心实现

useStore和Provider

import React, { useContext } from 'react';
import { useForceUpdate, useEffection } from './share';

type Selector = (store: T) => S;const StoreContext = React.createContext(null);const useStoreContext = () => {const contextValue = useContext(StoreContext);if (!contextValue) {throw new Error('could not find store context value; please ensure the component is wrapped in a ',
    );
  }return contextValue;
};/**
 * 在组件中读取全局状态
 * 需要通过传入的函数收集依赖
 */export const useStore = (selector: Selector): S => {
  const forceUpdate = useForceUpdate();
  const store = useStoreContext();
  const effection = useEffection(() => selector(store), {
    scheduler: forceUpdate,
    lazy: true,
  });
  const value = effection();
  return value;
};
export const Provider = StoreContext.Provider;

这个option是传递给Vue3的effectapi,

scheduler规定响应式数据更新以后应该做什么操作,这里我们使用forceUpdate去让组件重新渲染。

lazy表示延迟执行,后面我们手动调用effection来执行


{
  scheduler: forceUpdate,
  lazy: true,
}

再来看下useEffectionuseForceUpdate

import { useEffect, useReducer, useRef } from 'react';
import { effect, stop, ReactiveEffect } from '@vue/reactivity';

export const useEffection = (...effectArgs: Parameters<typeof effect>) => {
  // 用一个ref存储effection
  // effect函数只需要初始化执行一遍
  const effectionRef = useRef();if (!effectionRef.current) {
    effectionRef.current = effect(...effectArgs);
  }// 卸载组件后取消effectconst stopEffect = () => {
    stop(effectionRef.current!);
  };
  useEffect(() => stopEffect, []);return effectionRef.current
};export const useForceUpdate = () => {const [, forceUpdate] = useReducer(s => s + 1, 0);return forceUpdate;
};

也很简单,就是把传入的函数交给effect,并且在组件销毁的时候停止effect而已。

流程

  1. 先通过useForceUpdate在当前组件中注册一个强制更新的函数。
  2. 通过useContext读取用户从Provider中传入的store。
  3. 再通过Vue的effect去帮我们执行selector(store),并且指定scheduler为forceUpdate,这样就完成了依赖收集。
  4. 那么在store里的值更新了以后,触发了scheduler也就是forceUpdate,我们的React组件就自动更新啦。

就简单的几行代码,就实现了在React中使用@vue/reactivity中的所有能力。

优点:

  1. 直接引入@vue/reacivity,完全使用Vue3的reactivity能力,拥有computed, effect等各种能力,并且对于Set和Map也提供了响应式的能力。后续也会随着这个库的更新变得更加完善的和强大。
  2. vue-next仓库内部完整的测试用例。
  3. 完善的TypeScript类型支持。
  4. 完全复用@vue/reacivity实现超强的全局状态管理能力。
  5. 状态管理中组件级别的精确更新。
  6. Vue3总是要学的嘛,提前学习防止失业!

缺点:

  1. 由于需要精确的收集依赖全靠useStore,所以selector函数一定要精确的访问到你关心的数据。甚至如果你需要触发数组内部某个值的更新,那你在useStore中就不能只返回这个数组本身。

举一个例子:

function Logger() {
  const logs = useStore((store: Store) => {
    return store.state.logs.map((log, idx) => (
      <p className="log" key={idx}>
        {log}p>
    ));
  });

  return (
    <Card hoverable><h1>控制台h1><div className="logs">{logs}div>Card>
  );
}

这段代码直接在useStore中返回了整段jsx,是因为map的过程中回去访问数组的每一项来收集依赖,只有这样才能达到响应式的目的。

源码地址

https://github.com/sl1673495/react-composition-api

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

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

相关文章

css的三个特性 背景透明设置

关于行内元素&#xff08;补充一点&#xff09; 行内元素只能容纳文本或其他行内元素。&#xff08;a特殊a里面可以放块级元素&#xff09; 例子&#xff1a; 关于行高tip: 选择器的嵌套层级不应大于3级&#xff0c;位置靠后的限定条件应尽可能的精确。 属性定义必须另起一行…

ccs安装多版本编译器离线_大数据分析:学习工具JDK,在线安装指南

hadoop是使用Java语言开发的并且Hadoop运行需要有Java环境的支持&#xff0c;因此在安装hadoop之前需要安装Java开发环境即JDK(Java Development Kit)。安装前首先向大家介绍以一下本文会用到的几个词&#xff1a;JAVA_HOME:一是为了方便引用&#xff0c;比如&#xff0c;JDK安…

asp.net尚未在web服务器上注册_最新版Web服务器项目详解 00 项目概述

点 击 关 注 上 方&#xff02;两猿社&#xff02;设 为&#xff02;置 顶 或 星 标&#xff02;&#xff0c;干 货 第 一 时 间 送 达。互 联 网 猿 | 两 猿 社TineyWebServerLinux下C轻量级Web服务器&#xff0c;助力初学者快速实践网络编程&#xff0c;搭建属于自己的服务器…

DBMS-基本概念

文件处理系统&#xff08;file-processing system&#xff09;的主要弊端&#xff1a; 数据冗余和不一致&#xff08;data redundancy and inconsistency&#xff09;、数据访问困难&#xff08;difficulty in accessing data&#xff09;、数据孤立&#xff08;data isolation…

python多大孩子可以学_孩子学编程最佳年龄是多大

儿童编程教育已经悄悄地掀起了编程低龄化的热浪。但是很多人会疑惑&#xff0c;到底孩子学编程最佳年龄是多少呢&#xff1f;下面小编就为大家解答一下。孩子学编程最佳年龄 首先&#xff0c;孩子的学习黄金时期是非常重要的。从6岁开始&#xff0c;大多数孩子都可以掌握对基本…

上传文件到华为云云服务器,上传文件到云服务器

上传文件到云服务器 内容精选换一换登录Windows操作系统的弹性云服务器时&#xff0c;需使用密码方式登录。因此&#xff0c;用户需先根据创建弹性云服务器时使用的密钥文件&#xff0c;获取该弹性云服务器初始安装时系统生成的管理员密码(Administrator帐户或Cloudbase-init设…

关于图片预加载的思考

引子&#xff1a; 很多时候&#xff0c;我们在写html页面的时候&#xff0c;当需要在页面中加入图片时&#xff0c;我们很自然地把图片直接用<img>标签放在了<body>里面&#xff0c;这本来是没有多大问题的。 但是当图片数量很多的时候&#xff0c;问题就来了。H…

SpringBoot03 项目热部署

1 问题 在编写springBoot项目时&#xff0c;经常需要修改代码&#xff1b;但是每次修改代码后都需重新启动&#xff0c;修改的代码才会生效 2 这么实现IDEA能够像Eclipse那样保存过后就可以自动进行刷新呢 将springBoot项目进行热部署即可 3 如何实现SpringBoot项目的热部署01 …

STM32实现流水灯

led.c #include"led.h"void Led_Init(void) {GPIO_InitTypeDef GPIO_VALUE; //???RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//???GPIO_VALUE.GPIO_ModeGPIO_Mode_Out_PP;//???? ????GPIO_VALUE.GPIO_PinGPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_…

jacob 报错 Can't co-create object

问题&#xff1a;开始时一切正常&#xff0c;后来什么都没该&#xff0c;出现Cant co-create object报错&#xff0c;即是创建不了ActiveXComponent wdnew ActiveXComponent("Word.Application"); 偶尔发现任务管理器中word进程居然达到10个&#xff0c;而我没有打开…

wxpython可视化_使用wxPython的绘图模块wxPyPlot进行数据可视化

[Python进阶(四十)-数据可视化の使用matplotlib进行绘图前言  matplotlib是基于Python语言的开源项目&#xff0c;旨在为Python提供一个数据绘图包。我将在这篇文章中介绍matplotlib A #-*- coding: utf-8 -*- ############################################################…

如何让浮动的元素换行??css

当你想要做成这种布局效果的时候 紫色框里面的内容那样 它是一个列表 li元素是块级元素 默认大小是父元素ul的宽 并且换行 如果li没有背景的话那就不用管了 可是问题来了它不但有背景 而且是根据文字自适应的宽高 这就是inline-block类型的功能了 那么想让li是inline-bl…

设置博客园标题样式

1.向博客园申请js权限 我们需要进入博客园自定义博客模板的页面&#xff0c;向博客园管理团队申请页面运行js的权限。【博客园】->【设置】->【博客设置】&#xff0c;点击页面上的js权限申请&#xff0c;然后填写申请的理由&#xff0c;耐心等几分钟&#xff0c;再刷新一…

关联查询mysql_《MySQL数据库》关联查询

原标题&#xff1a;《MySQL数据库》关联查询一、关联查询1、概念在查询数据时&#xff0c;所需要的数据不只在一张表中&#xff0c;可能在两张或多张表中。这个时候&#xff0c;需要同时操作这些表来查询数据&#xff0c;即关联查询。关联查询所涉及到的表与表之间都会存在有关…

NOIP2017年11月9日赛前模拟

最后一次NOIP模拟了 题目1&#xff1a;回文数字 Tom 最近在研究回文数字。  假设 s[i] 是长度为 i 的回文数个数&#xff08;不含前导0&#xff09;&#xff0c;则对于给定的正整数 n 有&#xff1a; 以上等式中最后面的括号是布尔表达式&#xff0c;Tom 想知道S[n] mod 2333…

mysql视图表怎么设置约束_MySQL一一sql的视图、索引、约束

一、视图本质上相当于一张**“虚拟表”**&#xff0c;可当作独立的一张表进行操作(增、删、改、查)** 作用&#xff1a;**** a)**可通过权限控制&#xff0c;只将“表中的少数列”暴露给数据库用户&#xff0c;而不让该用户直接操纵数据库中“实际表”** b)**…

Software Development Life Cycle

转载于:https://www.cnblogs.com/genezhao/p/6879848.html

ArrayAndString(数组和字符串)

1.实现一个算法&#xff0c;确定一个字符串的所有字符是否全都不同。假使不允许使用额外的数据结构&#xff0c;又该怎么处理&#xff1f; public class UniqueChars {public static void main(String[] args) {// TODO Auto-generated method stubString string "abcdef…

position 的属性值

理论上来说&#xff0c;全部 position 的取值有8个 包括&#xff1a;position&#xff1a;static | relative | absolute | fixed | sticky | initial | inherit | unset 其中最常用的是 static 、relative、absolute、fixed 和 sticky initial、inherit、unset 是css的关键…

mysql数据库应用的权限层级_MySQL数据库的用户权限管理

嗨&#xff01;各位小伙伴今天翻了一下历史记录MySQL 数据库还有点内容今天开始我们就来补上吧~用户权限管理伙伴们要知道&#xff0c;在数据库方面有两个方向。一个是数据库管理员(Database Administrator)简称DBA&#xff0c;一个是数据库开发工程师(Database Developer)&…