gpt4 book ai didi

reactjs - 从 useCallback 访问状态变量时,值不会更新

转载 作者:行者123 更新时间:2023-12-01 23:54:53 24 4
gpt4 key购买 nike

在我的代码的某个地方,我正在从回调(UserCallback)访问我的组件的状态变量,我发现状态变量没有从初始值更新并回调指的是初始值。正如我在文档中读到的那样,当变量作为数组项之一传递时,它应该在更新时更新函数。以下是示例代码。


const Child = forwardRef((props, ref) => {
const [count, setCount] = useState(0);
const node = useRef(null);

useImperativeHandle(ref, () => ({
increment() {
setCount(count + 1);
}
}));

const clickListener = useCallback(
e => {
if (!node.current.contains(e.target)) {
alert(count);
}
},
[count]
);

useEffect(() => {
// Attach the listeners on component mount.
document.addEventListener("click", clickListener);
// Detach the listeners on component unmount.
return () => {
document.removeEventListener("click", clickListener);
};
}, []);

return (
<div
ref={node}
style={{ width: "500px", height: "100px", backgroundColor: "yellow" }}
>
<h1>Hi {count}</h1>
</div>
);
});

const Parent = () => {
const childRef = useRef();

return (
<div>
<Child ref={childRef} />
<button onClick={() => childRef.current.increment()}>Click</button>
</div>
);
};

export default function App() {
return (
<div className="App">
<Parent />
</div>
);
}


我最初构建的是自定义确认模式。我有一个状态变量,它将 display:blockdisplay:none 设置为根元素。然后,如果在组件外部单击,我需要通过将状态变量设置为 false 来关闭模式。以下是原始函数。

  const clickListener = useCallback(
(e: MouseEvent) => {
console.log('isVisible - ', isVisible, ' count - ', count, ' !node.current.contains(e.target) - ', !node.current.contains(e.target))
if (isVisible && !node.current.contains(e.target)) {
setIsVisible(false)
}
},
[node.current, isVisible],
)

它不会关闭,因为 isVisible 始终是 false,这是初始值。

我在这里做错了什么?

为了进一步说明,以下是完整组件。

const ConfirmActionModal = (props, ref) => {

const [isVisible, setIsVisible] = useState(false)
const [count, setCount] = useState(0)

const showModal = () => {
setIsVisible(true)
setCount(1)
}

useImperativeHandle(ref, () => {
return {
showModal: showModal
}
});

const node = useRef(null)
const stateRef = useRef(isVisible);

const escapeListener = useCallback((e: KeyboardEvent) => {
if (e.key === 'Escape') {
setIsVisible(false)
}
}, [])

useEffect(() => {
stateRef.current = isVisible;
}, [isVisible]);

useEffect(() => {
const clickListener = e => {
if (stateRef.current && !node.current.contains(e.target)) {
setIsVisible(false)
}
};

// Attach the listeners on component mount.
document.addEventListener('click', clickListener)
document.addEventListener('keyup', escapeListener)
// Detach the listeners on component unmount.
return () => {
document.removeEventListener('click', clickListener)
document.removeEventListener('keyup', escapeListener)
}
}, [])

return (
<div ref={node}>
<ConfirmPanel style={{ display : isVisible ? 'block': 'none'}}>
<ConfirmMessage>
Complete - {isVisible.toString()} - {count}
</ConfirmMessage>
<PrimaryButton
type="submit"
style={{
backgroundColor: "#00aa10",
color: "white",
marginRight: "10px",
margin: "auto"
}}
onClick={() => {console.log(isVisible); setCount(2)}}
>Confirm</PrimaryButton>
</ConfirmPanel>
</div>

)

}

export default forwardRef(ConfirmActionModal)

最佳答案

您将函数 clickListener 分配给 document.addEventListener 在组件挂载上,此函数 has a closurecount 值上。

在下一次渲染时,count 值将过时。

解决它的一种方法是实现一个带有引用闭包的函数:

const Child = forwardRef((props, ref) => {
const [count, setCount] = useState(0);
const countRef = useRef(count);

useEffect(() => {
countRef.current = count;
}, [count]);

useEffect(() => {
// countRef.current always holds the most updated state
const clickListener = e => {
if (!node.current.contains(e.target)) {
alert(countRef.current);
}
};

document.addEventListener("click", clickListener);
return () => {
document.removeEventListener("click", clickListener);
};
}, []);
...
}

Edit fast-wood-stsrn

关于reactjs - 从 useCallback 访问状态变量时,值不会更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62932594/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com