Here I have created a toggle button re-rendering in parent components and in child components a plus button, and the counter requirement is that the toggle button re-rendering is a toggle button that will hide and show the components, but if any last counter is added, for example, from 0 to 2, after hiding and showing it should remain the same, like 2 and not 0, but the problem is that I am not able to hide the child components, so how do I hide and show the child components and maintain the child state?
Basically, by rendering the parent, I want to keep maintaining the child state value.
parent code is
import React, { useRef, useState, useMemo } from 'react';
import DropDown from './DropDown';
function App() {
const shouldRenderChild = useRef(true);
const [hide, setHide] = useState(false);
const toggleChildRendering = () => {
shouldRenderChild.current = !shouldRenderChild.current;
setHide(!hide);
};
const MemoizedDropDown = useMemo(() => {
return shouldRenderChild.current ? <DropDown /> : null;
}, []);
return (
<div>
<button onClick={toggleChildRendering}>
Toggle Child Rendering
</button>
{MemoizedDropDown}
</div>
);
}
export default App;
child code is
import React, { useState } from "react";
export default function DropDown() {
const [number, setNumber] = useState(0);
return (
<>
<button onClick={() => setNumber(number + 1)}>+</button>
<div>Child Component{number}</div>
</>
);
}
3
Answers
The main issue with your approach is that you’re using
useMemo
in the wrong way.useMemo
caches a calculated value (in this case, a component) and will only recompute this value if one of its dependencies changes. You should useuseMemo
to decide whether or not to render theDropDown
component, and as a dependency for that decision, usehide
.Here’s a more detailed solution:
Parent Component:
We’ll use
hide
as the state to determine if we should show the child. We’ll useuseMemo
to cache the child component. Ifhide
changes, then it will recompute whether to show the child component.Child Component:
This remains the same. It simply shows a button to increment a counter.
By following this approach, you can show/hide the child component without it losing its internal state. However, note that if the child component gets unmounted (i.e., completely removed from the DOM) and then remounted, its internal state will be reset. If you want to persist the state even if the component is unmounted, you’ll need a more complex solution, such as using context or moving the state up to the parent component and passing it down as props.
Every time component is mounted or unmounted (using conditional rendering) – state of the component is re-setting itself to default values. So that is your problem, and
useMemo
hook will not help you here.To solve your issue you can modify your root css files and add something like
.hidden
class:Most probably you already have that kind of utility classes in your project. Like
d-none
for bootstrap or similar.Next – you need to add a wrapper to your
DropDown
component, or change the type of root node that is returned by this component to something else thanReact.Fragment
ie<> </>
and to apply conditionalclassName
on it like this:Here is an example of the solution:
Stackblitz demo
The simple solution to this is Lifting the State up.
!hide ? <DropDown /> : null
, it’d be unmounted whenhide
is true.number
in child’s state, it would also be vanished.number
value in parent’s state, it’d persist.hide
flag being true, you still got the state saved in parent.hide
flag for styling the child component todisplay: none
<Dropdown style={{
display: hide ? ‘none’ : ‘block’
}} … />
The right way:
App.js
Dropdown.js