概述
一种具备响应式的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>
  );
};




![[Linux] Linux操作系统 进程的优先级 环境变量(一)](https://i-blog.csdnimg.cn/direct/7b78d48182fc4735a04f1c824cbc2ac4.png)












