**一. 问题**
const [contentParams, setContentParams] = useState({
title: "资料",
mode: 'edit', // edit/read,
editParams: {
extra: [
<Button
key="save"
type="primary"
icon="save"
onClick={saveHandler}
style={{ marginLeft: '8px' }}
>
保存
</Button>
],
},
readParams: {
extra: [],
}
// baseInfoCardData:[]
})
<Card>
<Button onClick={contentParams.editParams.extra[0]}>点击事件</Button>
</Card>
const saveHandler =()=>{
const params = {
structureMain,
structureHistoryList,
contactList,
customerBaseInfo: {},
customerAuthInfo: {}
}
}
structureMain,structureHistoryList,contactList发生改变的方式是赋值形式(即变量引用发生改变)。
其中structureMain,structureHistoryList,contactList,等是外部状态变量,问题出在当这三个变量发生改变时,onClick方法无法获取最新的值。
## 二. 解答
这个方法中,contentParams中OnClick={saveHandler}中saveHandler实际上是函数的定义而非执行,这种写法就相当于在useState的匿名函数中写了一个saveHandler函数,因为这个函数也用到了外部变量,所以这个地方的saveHandler是一个闭包函数。
由于闭包函数的特性(无法直接访问外部变量环境,而只能通过引用来访问),这个闭包函数在创建的时候,记录了structureMain,structureHistoryList和contactList的引用,而这三个变量发生改变是通过重新赋值进行的,变量的引用已经发生了改变,但是此闭包函数使用的仍然是创建时的引用(即旧的引用),因此获取到的始终是未发生改变的值,才导致了此问题。
要解决这个问题,思路上可以考虑想办法重新创建这个闭包函数,使得其获取到最新的引用;也可以调整structureMain,structureHistoryList和contactList的变量的改变方式,从赋值形式改成属性赋予,这样它的引用就没有改变,闭包函数就能通过作用域链获取到最新的值;还可以改变函数的调用方式,使这个函数变成非闭包函数。
此次问题采用的解决方案是改为非闭包函数。并通过useEffect间接触发保存方法。
部分代码如下:
const [methodDispatchParams, setMethodDispatchParams] = useState({ saveHandlerDispatch: 0 })
……
<Button
key="save"
type="primary"
icon="save"
// loading={updateLoading}
// disabled={!isShowSave}
onClick={()=>setMethodDispatchParams({...methodDispatchParams, saveHandlerDispatch: 1})}
style={{ marginLeft: '8px' }}
>
保存
</Button>,
……
useEffect(()=>{
// 函数调用(一次useEffect只调用一次函数,防止出现套娃问题,所以执行完后return中断)
const { saveHandlerDispatch } = methodDispatchParams
if(saveHandlerDispatch === 1){
// 点击事件
saveHandler()
setMethodDispatchParams({...methodDispatchParams,saveHandlerDispatch: 0})
return;
}
},[methodDispatchParams])
## 三. 说明
1. js中变量赋值的两种形式的区别
let a = {}
(1) 赋值形式,引用发生改变
a = {age: 15}
(2) 属性赋予形式,引用不变
a.age = 15
2. 数组解构方法
const array = [1,5,7]
const [age1, age2, age3=17 ] = array
结果age1 = 1, age2=5, age3 = 7
其中age3=17是的意思是赋予age3一个默认值17
3. 闭包函数的充分必要条件
(1) 定义在另一个函数内部
(2) 使用了闭包函数外的变量
4. 闭包函数的特征
(1) 创建时记录捕获外部变量的引用,形成了自己的私有环境
(2) 无法直接访问外部环境(这意味着只能通过引用来访问外部变量,一旦引用发生变化,就无法感知到外部变量的变化)
5. 外部变量感知的原理
这种感知的实现原理主要是JavaScript的作用域链和闭包机制。
作用域链:JavaScript中有一个概念叫做“作用域链”,当代码运行时,JavaScript引擎会创建一个执行上下文,这个上下文中包含一个变量对象,存储着当前作用域内的变量和函数信息。当函数访问一个变量时,JavaScript引擎会沿着作用域链向上查找这个变量,直到找到为止。所以,当一个函数引用了外部变量,实际上是通过作用域链来访问这些变量的。
闭包机制:当你在一个函数内部定义另一个函数,内部函数可以访问其包含函数(外部函数)的变量,即使外部函数已经执行完毕。这是因为内部函数形成了一个闭包,闭包中包含了对外部函数变量的引用。所以,即使外部变量的值改变了,闭包函数依然能够感知到,因为它保留了对旧变量的引用。
综上所述,这种感知的实现原理就是,通过作用域链查找变量,通过闭包机制保留对变量的引用。这两者共同作用,使得函数能够感知到外部变量的变化。
js闭包函数引发的点击事件变量最新值获取问题以及解决方案
于 2023-11-24 17:44:41 首次发布