author @栾树
背景
我有一个朋友
在看完 Vue3.0 原始值的响应式方案
这篇文章之后给了我一些建议,大意就是让我推出一个系列文章,思来想去,这几年时间里也未曾有文章沉淀,从本篇文章开始,我将针开阔编程思想,将自己的一些奇思妙想给落地下来。由于文章落地较少缺少写作技巧,目前的想法是慢慢来,通过大量写作来提升,加油冲鸭!
本文将基于Vue3.0
实现React Hooks
中useState
函数,后续接着实现以下函数
- useReducer
- useReactive
- useCallback
- useStorage
- useLocalStorage
- 暂时还在构思中 系列不会停...
前言
实现React Hooks
中useState
函数之前,我们需要大致了解useState
函数是干嘛的,在React出现hooks方案后,函数式组件就有了自己的状态
// React18的版本已经不需要引入React
import React, { useState } from 'react';
function App() {
const [count, setCount] = useState(0);
return (
<div>
<h1>count: {count}</h1>
{/* 闭包形式更新 */}
<button onClick={() => setCount(count + 1)}>+1</button>
{/* 函数式更新 */}
<button onClick={() => setCount((val) => val + 2)}>+2</button>
</div>
);
}
export default App;
基础版
useState()
默认接受一个参数useState( (val)=> val+1 )
更新函数支持函数作为参数,也支持直接传入值进行更新设置,那么我们可以通过反推落地useState
函数。
function useState(initialState){
// 初始化 state
let state = initialState;
// update 函数
const dispatch = (newState)=>{
// 1.如果 newState 是函数我们则手动调用
if(typeof newState === 'function'){
state = newState(newState);
}else{
// 2.否则直接赋值
state = newState;
}
}
// 返回
return [state, dispatch]
}
export default useState
Vue3.0中使用
<template>
<h1>{{ count }}</h1>
<button @click="setCount(2)">+1</button>
</template>
<script>
import useState from '@/hooks/useState.js';
export default {
setup(){
const [count, setCount] = useState(0);
return {
count,
setCount
}
}
};
</script>
思考
- 运行效果中,点击按钮数据未发生改变,这是为什么呢?
- 如果我们此时多调用几次
useState
怎么保存对应的状态呢? - 有了这些问题之后我们该怎么进行设计呢?
抱着这些问题我们继续探索
useState
将在最终版本中实现
最终版
- 由于业务中
useState
会被多次使用,那么我们需要缓存其对应的状态,这里使用数组缓存,这里我也考虑过对象
或者Map
数组的形式稍微好理解一些 - 由于不停的缓存,当体积使用越多则存储的状态则越大,这个目前还在思考🤔有待改善,有考虑过
Weakmap
落引用?这种暂未实现
import {ref} from "vue";
// states 存储 initialState 状态
// stateSetters 存储 dispatch 函数
// stateIndex 默认从0开始
let [states, stateSetters, stateIndex] = [[], [], 0]
/**
* 创建 createState
* */
function createState(initialState, stateIndex) {
// 响应式数据包装
const state = ref(initialState);
// 如果states中存在则直接返回 否则返回包装的ref数据
return states[stateIndex] !== undefined ? states[stateIndex] : state;
}
/**
* 创建 createSetter
*/
function createSetter(stateIndex) {
// 返回一个函数 这里其实玩了一手 闭包的概念 《stateIndex》被返回的函数引用了
return function (newState) {
if (typeof newState === "function") {
// 如果为函数则手动调用 并且更新states对应值
states[stateIndex].value = newState(states[stateIndex].value);
} else {
// 直接更新states对应值
states[stateIndex].value = newState;
}
};
}
// 业务中 useState 可以多次 所以就需要使用队列
function useState(initialState) {
// 创建 state
states[stateIndex] = createState(initialState, stateIndex);
// 如果没有则直接进行添加
if (!stateSetters[stateIndex]) {
stateSetters.push(createSetter(stateIndex));
}
const _state = states[stateIndex];
const _setState = stateSetters[stateIndex];
// 每次调用 useState 则会++1
stateIndex++;
return [_state, _setState];
}
export default useState;