I am writing a modal component in React. The logic is simple, first pass the variable visible
as props to the component modal. Then the external component updates the state of the modal by controlling visible. But I got an issue where modal didn’t turn off after the external component set visible to false, and it didn’t seem like setState
was in effect.
I have provided the complete demo code, you can see the log that the visible object is not change as I set a new Object to it.
Code sandbox click to see demo
I hope you can help me look at this problem, thank you very much!
export default function () {
const [visible, setVisible] = useState(() => ({
m1: false,
m2: false,
m3: false
}));
const close = useCallback(() => {
const s = { ...visible, m1: false };
console.log("should be: ", s);
setVisible(s);
}, []);
useEffect(() => {
console.log("newVal: ", visible);
}, [visible]);
const props = {
components: [
{
component: (
<button
onClick={() => {
setVisible({ ...visible, m1: true });
}}
>
open Modal1
<Modal visible={visible.m1} close={close}>
this is a modal
</Modal>
</button>
)
}
]
};
return <IntroComponent {...props} />;
}
2
Answers
You need to call event.stopPropagation() in order to prevent the button event from opening modal message up again.
This is because your Modal is not actually a Modal. It is a child of
<button {...}/>
, likewise whenbutton
is clicked, it will makem1: false
, but the event bubbles up to the Modal onClick handler makingm1: true
again.When you change the DOM structure you can see it works just fine. (side note:
<>
is syntactic sugar for fragments). So this will work:@John has the right idea, and while it technically solves the issue, it is more a workaround than an actual desirable outcome. See Q: When is good practice to use stopPropagation()?
Also when I see language like Modal, for your consideration is the ReactPortal. However, it is a complex topic that requires some understanding about how the DOM and VDOM interact, and beyond the scope of this Q/A.
View CodeSandbox