Outdated Ref value in nested React component

36 Views Asked by At

So I have a container component that can expand to fit some content, and it should also expand when nested containers are expanded. Ive created this Codepen with the minimal components to demonstrate the problem

the component:

const ExpandableContainer = ({ expanded, children , name }) => {
    const ref = useRef(null)

    const [style, setStyle] = useState({"maxHeight": 0})

    useLayoutEffect(() => {
        console.log(name, ref.current.children[0].clientHeight)
        setStyle(expanded ? {'maxHeight':ref.current.getBoundingClientRect().height} : {'maxHeight':0})
    },[children,expanded])

    return (
        <div style={style} className='overflow-hidden ease-in-out transition-all duration-500'>
            <div ref={ref}>
                {children}
            </div>
        </div>
    )
}

what im trying to make:

const TestComponent = () => {
    const [parentOpen, setParentOpen] = useState(false)
    const [childOpen, setChildOpen] = useState(false)

    return (
        <div>
        
            <button onClick={() => setParentOpen((prev) => !prev)} className='p-3 bg-red-300 m-4'>Open Parent</button>
            <button onClick={() => setChildOpen((prev) => !prev)} className='p-3 bg-red-300 m-4'>Open Child</button>

            <p className='m-2 p-2 bg-blue-300'>is parent open: {parentOpen?'true':'false'}</p>
            <p className='m-2 p-2 bg-blue-300'>is child open: {childOpen?'true':'false'}</p>

            <ExpandableContainer expanded={parentOpen} name='parent'>
                <div className='bg-green-300 p-2'>
                    <p>hello</p>
                    <p>some content</p>

                    <ExpandableContainer expanded={childOpen} name='child'>
                        <div className='bg-purple-300 p-2'>
                            <p>hello</p>
                            <p>some content</p>
                        </div>
                    </ExpandableContainer>

                </div>

            </ ExpandableContainer>
        
        </div>
    )
}

this works perfectly fine with the parent component, it expands and closes to the correct size. but on the child component, with the parent open, clicking to open the child does not affect the size of the parent, but the childs size is correct. clicking the button again to close the child makes the parent take the size that it should be when the child is open.

initial

parent, props - expanded:false , state {maxHeight:0}
child, props - expanded:false , state {maxHeight:0}

parent open button clicked:

parent, props - expanded:true, state {maxHeight:64}
child, props - expanded:false , state {maxHeight:0}

child open button clicked:

parent, props - expanded:true, state {maxHeight:64}  <- should be 128
child, props - expanded:true, state {maxHeight:64}

child close button clicked:

parent, props - expanded:true, state {maxHeight:128} <- should be 64
child, props - expanded:false , state {maxHeight:0}

If the child is opened before the parent, opening the parent will show the correct result

Ive tried changing useEffect to useLayoutEffect to no avail. After some debugging I found that potentially the parent is rendered before the child so the value for ref is set before the child is able to resize but im not 100% sure

0

There are 0 best solutions below