Inside a Component, I have a Modal from which the user can do an update to some data through an API and then change the state of that main Component. Because there is a change in state, everything will re-render.
I would like to keep the modal open, so that I can show a success message.
The code would be something like this.
const Main = () => {
const [state, setState()] = useState();
return (
<Component state={state}>
<Modal onButtonClick={() => {
updateThroughApi().then(() => setState())} />
</Component>
)
}
When user clicks on modal's button, the state changes, and Component
is re-rendered. Modal is rendered too, as it's inside.
I have thought of two possible solutions:
Move the modal outside of the ponent. This is a problem, as my actual code is not as simple as the example I posted. In my code, the modal opens on the click of a button
B
, which is deep insideComponent
. So, if I move the modal out fromComponent
, I would have to pass the status and the action to change status (e.g. [open, setOpen]) through several ponents until button B (prop drilling).Another solution: On the action
onButtonClick
I just do the API update, and use a new stateupdated = true
; then,onModalClose
, only if updated is true, I runsetState
soComponent
is rendered just after the modal is closed. But this solution seems a hack to me. There must be a better way.
Is there any better solution?
Inside a Component, I have a Modal from which the user can do an update to some data through an API and then change the state of that main Component. Because there is a change in state, everything will re-render.
I would like to keep the modal open, so that I can show a success message.
The code would be something like this.
const Main = () => {
const [state, setState()] = useState();
return (
<Component state={state}>
<Modal onButtonClick={() => {
updateThroughApi().then(() => setState())} />
</Component>
)
}
When user clicks on modal's button, the state changes, and Component
is re-rendered. Modal is rendered too, as it's inside.
I have thought of two possible solutions:
Move the modal outside of the ponent. This is a problem, as my actual code is not as simple as the example I posted. In my code, the modal opens on the click of a button
B
, which is deep insideComponent
. So, if I move the modal out fromComponent
, I would have to pass the status and the action to change status (e.g. [open, setOpen]) through several ponents until button B (prop drilling).Another solution: On the action
onButtonClick
I just do the API update, and use a new stateupdated = true
; then,onModalClose
, only if updated is true, I runsetState
soComponent
is rendered just after the modal is closed. But this solution seems a hack to me. There must be a better way.
Is there any better solution?
Share Improve this question asked Jan 11, 2021 at 17:05 de3de3 2,0006 gold badges25 silver badges42 bronze badges 1- Full code please. Something is fundamentally wrong with your code. – Andrew Commented Jan 11, 2021 at 17:13
2 Answers
Reset to default 3Something is obviously going very wrong here, the Modal should not close. As a workaround you could do something like this:
const Main = () => {
const [state, setState()] = useState();
const modal = useMemo(() => (
<Modal onButtonClick={() => {
updateThroughApi().then(() => setState())} />
), [])
return (
<Component state={state}>{modal}</Component>
)
}
Your Modal
is re-rendering because your function passed as onButtonClick
is redefined at every render.
So you have 2 options here:
1/ Keep your Modal
inside your Component and use useMemo
import { useMemo } from 'react'
const Main = () => {
const [state, setState] = useState();
const modal = useMemo(() => (
<Modal onButtonClick={() => (
updateThroughApi().then(() => setState())
)}
/>
), [])
return (
<Component state={state}>
{modal}
</Component>
)
}
Or 2/ Move your Modal
outside your ponent and use bination of memo
and useCallback
import { memo, useCallback } from 'react'
const Main = () => {
const [state, setState] = useState();
const onButtonClick = useCallback(() => updateThroughApi().then(() => setState()), []);
return (
<Component state={state}>
<Modal onButtonClick={onButtonClick} />
</Component>
)
}
const Modal = memo(({onButtonClick}) => {
})
So in this case, at every render, memo
will pare if all Modal
props are ===
from previous render, which is now the case, because memoization of onButtonClick
with useCallback
, and so your Modal
ponent will not re-render
https://reactjs/docs/hooks-reference.html#usememo