admin管理员组文章数量:1415075
Popular ponent renders the Modal, but it will be visible depending on the modal.isVisible Boolean state. My goal is to attach a mouseevent listener and a handler, to close the Modal when clicking outside of it. I tried to do it using useRef, and pass the ref to the Modal dialog, in order to capture the event and check if clicked outside using event.target.contains
.
The problem is, on first render I do not want to assign 'mousedown' handler to the document, but only when wrapper && wrapper.current
is defined and it outside the Modal dialog. on first render, I can see that the effect runs as expected, but when expanding the Modal by setting isVisible -> true
, the ref.current
should have changed, but the effect doesn't run again, it will run again if I close the Modal, and then it will work as expecetd. The ref.current
changes it not reflected in the effect, even though the effect is supposed to run after the DOM update. Why is that?
const Modal = ({ isVisible, repo, onClose }) => {
const wrapper = useRef();
console.count('render modal');
const escapeHandler = useCallback(({ key }) => {
if (key == 'Escape') onClose();
}, [onClose]);
useEffect(() => {
document.addEventListener('keydown', escapeHandler);
return () => document.removeEventListener('keydown', escapeHandler);
}, []);
useEffect(() => {
// runs after first render, but when setting isVisible to true and causing a rerender
// the effect doesn't run again despite ref.current is changed to <div>
// only after closing the Modal with Escape, it will work as expected, why?
console.count('effect modal');
console.log(wrapper.current);
}, [wrapper.current]);
return !isVisible ? null : (
<div className="modal">
<div className="modal-dialog" ref={wrapper}>
<span className="modal-close" onClick={onClose}>×</span>
{repo && <pre>{JSON.stringify(repo, null, 4)}</pre>}
</div>
</div>
);
};
const Popular = () => {
const [modal, setModal] = useState({ isVisible: false, repo: null });
const closeModal = useCallback(() => {
setModal({ isVisible: false, repo: null });
}, []);
return <Modal onClose={closeModal} {...modal} />
};
However, after reading the docs, if I use a useCallback and pass it as a ref, it works as expetced, like so, why?
const wrapper = useCallback(node => {
// works as expected every time the ref changes
console.log(node);
}, []);
Let me know if the question phrasing is a bit unclear, I'll try to explain a little better
Popular ponent renders the Modal, but it will be visible depending on the modal.isVisible Boolean state. My goal is to attach a mouseevent listener and a handler, to close the Modal when clicking outside of it. I tried to do it using useRef, and pass the ref to the Modal dialog, in order to capture the event and check if clicked outside using event.target.contains
.
The problem is, on first render I do not want to assign 'mousedown' handler to the document, but only when wrapper && wrapper.current
is defined and it outside the Modal dialog. on first render, I can see that the effect runs as expected, but when expanding the Modal by setting isVisible -> true
, the ref.current
should have changed, but the effect doesn't run again, it will run again if I close the Modal, and then it will work as expecetd. The ref.current
changes it not reflected in the effect, even though the effect is supposed to run after the DOM update. Why is that?
const Modal = ({ isVisible, repo, onClose }) => {
const wrapper = useRef();
console.count('render modal');
const escapeHandler = useCallback(({ key }) => {
if (key == 'Escape') onClose();
}, [onClose]);
useEffect(() => {
document.addEventListener('keydown', escapeHandler);
return () => document.removeEventListener('keydown', escapeHandler);
}, []);
useEffect(() => {
// runs after first render, but when setting isVisible to true and causing a rerender
// the effect doesn't run again despite ref.current is changed to <div>
// only after closing the Modal with Escape, it will work as expected, why?
console.count('effect modal');
console.log(wrapper.current);
}, [wrapper.current]);
return !isVisible ? null : (
<div className="modal">
<div className="modal-dialog" ref={wrapper}>
<span className="modal-close" onClick={onClose}>×</span>
{repo && <pre>{JSON.stringify(repo, null, 4)}</pre>}
</div>
</div>
);
};
const Popular = () => {
const [modal, setModal] = useState({ isVisible: false, repo: null });
const closeModal = useCallback(() => {
setModal({ isVisible: false, repo: null });
}, []);
return <Modal onClose={closeModal} {...modal} />
};
However, after reading the docs, if I use a useCallback and pass it as a ref, it works as expetced, like so, why?
const wrapper = useCallback(node => {
// works as expected every time the ref changes
console.log(node);
}, []);
Let me know if the question phrasing is a bit unclear, I'll try to explain a little better
Share Improve this question asked May 5, 2020 at 10:41 spider monkeyspider monkey 3341 gold badge4 silver badges11 bronze badges1 Answer
Reset to default 5I am not really sure why despite adding wrapper.current
to dependency and ponent re-rendering due to prop change, the useEffect isn't running the first time but is called on all subsequent isVisible state changes. It could be a bug in react. Maybe you can create a issue on reactjs for that.
That said,
However all the other observations you have are justified. Making use of useCallback call the function everytime because in that way you assign the ref using the ref callback pattern wherein ref is assigned like ref={node => wrapper.current = node}
.
This way everytime your visible state is true the ref callback is called resulting in the useCallback function to be called if you use it like
const modelRef= useRef(null);
const wrapper = useCallback((node) => {
modelRef.current = node;
console.log(node);
}, [])
...
<div ref={wrapper} />
The above useEffect code will however run correctly and deterministically if you add isVisible
as a dependency to useEffect instead of wrapper.current
useEffect(() => {
console.count('effect modal');
console.log(wrapper.current);
}, [isVisible])
and that is a correct way to go too since you ref is changed only when the isVisible flag is changed and depending on a mutable value as a useEffect
is not a great solution as there may arise some cases when the mutation isn't acpanied a re-render at the same time and the useEffect
won't run at all in that case
本文标签:
版权声明:本文标题:javascript - ref.current in useEffect deps doesn't work when expected (compared to useCallback ref) - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1745231042a2648824.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论