skip to Main Content

New to programming:, have built a collapsable accordion in an NextJs app using the react-collapse package. It works as intended, the problem comes with how it is tracking what ‘drawer’ is currently open. I’m trying to, in a sibling have a different image shown per drawer.

currently I iterate through an array of items to generate each section:

{
    accordionData.map((item, idx) => {
        return (
            <AccordionItem
              key={idx}
              open={idx === open}
              title={item.title}
              desc={item.desc}
              link={item.link}
              url={item.url}
              toggle={() => toggle(idx)}
            />
        )
    })
}

I have a useState hook:

    const [open, setOpen] = useState(0);

and my toggle function looks like:

const toggle = (idx) => {
    if(open === idx) {
        return setOpen(null)
    }
    console.log(idx)  
    setOpen(idx)  
    console.log(open)
}

What I want is for example: when the first instance of is open to have an image displayed in a div completely outside of the accordion, and then as the accordion items are opened the image changes.

I assumed I would do this by conditional rendering:

if open == 0 then render imageX else if open == 1 then render imageY …and so on

problem is that open is never set to the correct index of my array and counterintuitively its also never wrong in a predictable manner so I could write something like if open = (0+1) as its index is changing every time its opened. As per my console logs above my first one console.log(idx) is always correct, however the following one console.log(open) is always wrong.

2

Answers


  1. setOpen is asynchronous in React. When you call setOpen, React doesn’t immediately update the state, so the subsequent console.log(open) won’t reflect the updated state immediately after the setOpen call.

    You can test with this:

    const toggle = (idx) => {
        if (open === idx) {
            return setOpen(null);
        }
    
        setOpen(idx, () => {
            console.log(open);
        });
    };
    

    But,open won’t be immediately updated where you call setOpen because state updates are asynchronous. If you need to perform actions based on the updated state, you should use the callback passed to setOpen.

    Now, you can conditionally render your images based on the value of open:

    {open === 0 && <img src={imageX}/>}
    {open === 1 && <img src={imageY}/>}
    
    Login or Signup to reply.
  2. I believe the best approach is to use the useEffect hook.

    You’re right. The state doesn’t get updated immediately after the call, but you can react to that change once it happens.

    Here is an example of the useEffect hook:

    useEffect(() => {
      toggle(open);
    }, [open]);
    

    If you only use the toggle function to react to changes in the open state and not in any other case, you can write the entire function directly within the useEffect hook.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search