javascript - 无论如何设置一个变量等于每次访问变量时调用的函数
问题描述
我正在尝试从 React 重新创建 'useState' 钩子作为一个愚蠢有趣的个人练习,但是在从我的“全局状态”访问变量时遇到了麻烦。我知道这在 react 之外没有实际用途,但我只是认为无论如何都可以尝试。
目前我在下面有以下实现,但是因为解构变量仅在第一次设置并且在使用相应的设置器时没有更新,所以它总是会返回相同的变量。我完全理解为什么会发生这种情况,但我不确定是否有办法让它工作,或者这是否是一个失败的原因。解构的 setter 确实更新了全局状态,但变量当然如前所述不会再次访问全局状态,因为它只是在初始时间设置。
let PROP_ID = 0;
let GLOBAL_STATE = {};
const useState = prop => {
const id = PROP_ID++;
GLOBAL_STATE[id] = prop;
return [
(() => {
return GLOBAL_STATE[id];
})(),
function(nv) {
GLOBAL_STATE[id] = nv;
}
];
};
const [userName, setUserName] = useState("Chris");
const [favCol, setFavCol] = useState("red");
console.log(GLOBAL_STATE);
console.log(userName);
setUserName("Bob");
console.dir(GLOBAL_STATE);
console.log(userName);
我只想知道是否有办法将解构的引用变量设置为某种函数,当引用该变量时,总是会调用该函数以从全局状态获取新变量。
解决方案
我认为你在这里遗漏了一块拼图。
React 钩子取决于它们在给定功能组件中的调用位置。如果没有封装函数,您将删除钩子提供的状态的有用性,因为它们在您的示例中只被调用一次,因此解构语法中的引用永远不会像您观察到的那样更新。
让我们让它们在函数的上下文中工作。
const { component, useState } = (function () {
const functions = new WeakMap()
const stack = []
let hooks
let index
function component (fn) {
return function (...args) {
try {
stack.push({ hooks, index })
hooks = functions.get(fn)
index = 0
if (!hooks) {
functions.set(fn, hooks = [])
}
return fn.apply(this, args)
} finally {
({ hooks, index } = stack.pop())
}
}
}
function useState (initialValue) {
const hook = index++
if (hook === hooks.length) {
hooks.push(initialValue)
}
return [
hooks[hook],
function setState (action) {
if (typeof action === 'function') {
hooks[hook] = action(hooks[hook])
} else {
hooks[hook] = action
}
}
]
}
return { component, useState }
})()
const fibonacci = component(function () {
const [a, setA] = useState(1)
const [b, setB] = useState(1)
setA(b)
setB(a + b)
return a
})
const sequence = component(function () {
const [text, setText] = useState('')
setText(
text.length === 0
? fibonacci().toString()
: [text, fibonacci()].join()
)
return text
})
for (let i = 0; i < 20; i++) {
console.log(sequence())
}
这里的stack
变量允许我们嵌套有状态的函数调用,并且该hooks
变量通过当前执行component
的stack
.
component()
这种实现可能看起来过于复杂,但重点stack
是部分模仿 React 框架如何处理功能组件。这仍然比 React 的工作方式简单得多,因为我们将同一函数的所有调用视为函数组件的同一实例。
另一方面,在 React 中,一个特定的函数可以用于多个不同的实例,可以根据许多因素(例如虚拟 DOM 在层次结构中的位置、thekey
和ref
props 等)相互区分,所以它是比这复杂得多。
在我看来,您只是想让您的示例正常工作。为此,您需要做的就是将变量更改为 getter 函数:
const useState = state => [
() => state,
value => { state = value }
];
const [getUserName, setUserName] = useState('Chris');
const [getFavCol, setFavCol] = useState('red');
console.log(getUserName());
setUserName('Bob');
console.log(getUserName());
比您拥有的要简单得多,并且不需要任何全局变量即可工作。
如果手动 getter 看起来太不方便,那么您就无法解构,但您可以实现一种几乎同样易于使用的方法:
const useState = state => ({
get state () { return state },
set (value) { state = value }
});
const userName = useState('Chris');
const favCol = useState('red');
console.log(userName.state);
userName.set('Bob');
console.log(userName.state);
推荐阅读
- python - 如何构造一个 XPath 以从父 ul 标记中提取 url www.fastreact.co.uk?
- mysql - 如何将 SQL 查询的结果输出到报表表格中?
- typescript - tsc 时 webpack 无法捆绑文件
- excel - 取消合并表以从两列创建唯一值
- ios - 在 iOS App 中以指定语言显示系统 UI
- python - Tkinter 网格系统:排列元素
- python - python缺少1个必需的位置参数
- php - 未捕获的错误:在 C 中找不到类 'PHPMailer\PHPMailer\Exception'
- jmeter - 有没有办法以编程方式确定 JMeter 是否在“功能测试模式”下运行?
- python - 将数据列表列表转换为python中的矩阵(检查内部)