Home > Back-end >  How can I combine multiple state values into one useState?
How can I combine multiple state values into one useState?

Time:07-26

function Nav(){

   const [toggleDropDownAbout, setToggleDropDownAbout] = useState(fasle);
   const [toggleDropDownCareers, setToggleDropDownCareers] = useState(fasle);
   const [toggleDropDownNew, setToggleDropDownNew] = useState(fasle);

  return(
  
    <Link to='/' className='flex items-center text-slate-500 font-medium text-base px-3 py-3 nav-item hover:text-red-700 relative gap-1 '>About Us <FaCaretDown className='rotate'/></Link>
     {toggleDropDownAbout &&
      <ul className='grid pl-6 dropdown-item bg-red-100 lg:absolute top-12 z-10'>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>History</Link>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>Quality Policy</Link>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>Vision / Mission</Link>
       </ul>
      }
      
      <Link to='/' className='flex items-center text-slate-500 font-medium text-base px-3 py-3 nav-item hover:text-red-700 relative gap-1 '>Careers <FaCaretDown className='rotate'/></Link>
     {toggleDropDownCareers &&
      <ul className='grid pl-6 dropdown-item bg-red-100 lg:absolute top-12 z-10'>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>History</Link>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>Quality Policy</Link>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>Vision / Mission</Link>
       </ul>
      }
      
      <Link to='/' className='flex items-center text-slate-500 font-medium text-base px-3 py-3 nav-item hover:text-red-700 relative gap-1 '>Whats new <FaCaretDown className='rotate'/></Link>
     {toggleDropDownNew &&
      <ul className='grid pl-6 dropdown-item bg-red-100 lg:absolute top-12 z-10'>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>History</Link>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>Quality Policy</Link>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>Vision / Mission</Link>
       </ul>
      }
  
  )
}

It's running but is there a way to use one useState or some other way to get the same result. I just dont think this code is good and i dont have any idea how to do this in other way

CodePudding user response:

useState allows you to declare an object as a value too. You can leverage that to define a single state.

function Nav(){

   const [toggleStates, setToggleStates] = useState({
       toggleDropDownAbout,
       toggleDropDownCareers,
       toggleDropDownNew
   });
   

  return(
  
    <Link to='/' className='flex items-center text-slate-500 font-medium text-base px-3 py-3 nav-item hover:text-red-700 relative gap-1 '>About Us <FaCaretDown className='rotate'/></Link>
     {toggleStates.toggleDropDownAbout &&
      <ul className='grid pl-6 dropdown-item bg-red-100 lg:absolute top-12 z-10'>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>History</Link>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>Quality Policy</Link>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>Vision / Mission</Link>
       </ul>
      }
      
      <Link to='/' className='flex items-center text-slate-500 font-medium text-base px-3 py-3 nav-item hover:text-red-700 relative gap-1 '>Careers <FaCaretDown className='rotate'/></Link>
     {toggleStates.toggleDropDownCareers &&
      <ul className='grid pl-6 dropdown-item bg-red-100 lg:absolute top-12 z-10'>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>History</Link>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>Quality Policy</Link>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>Vision / Mission</Link>
       </ul>
      }
      
      <Link to='/' className='flex items-center text-slate-500 font-medium text-base px-3 py-3 nav-item hover:text-red-700 relative gap-1 '>Whats new <FaCaretDown className='rotate'/></Link>
     {toggleStates.toggleDropDownNew &&
      <ul className='grid pl-6 dropdown-item bg-red-100 lg:absolute top-12 z-10'>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>History</Link>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>Quality Policy</Link>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>Vision / Mission</Link>
       </ul>
      }
  
  )
}

However when using the above approach you need to be mindful that updates using setToggleStates will overwrite the entire state value and not merge it. So you should spread and update the object like

setToggleState(prev => ({
   ...prev,
   toggleDropDownAbout: !prev.toggleDropDownAbout
}));

P.S. There is nothing wrong with keeping multiple states with useState in your component. Ideally, you should consider creating a single state with a value as object if the states are related and belong to a single property


Also depending on interaction, you can simplify the above code. In case your intention is to only open one dropdown at a time, you can store the id enum of the dropdown as state value and use it.

function Nav(){

   const [toggleState, setToggleState] = useState(null);


  return(

    <Link to='/' className='flex items-center text-slate-500 font-medium text-base px-3 py-3 nav-item hover:text-red-700 relative gap-1 '>About Us <FaCaretDown className='rotate'/></Link>
     {toggleState === 'about' &&
      <ul className='grid pl-6 dropdown-item bg-red-100 lg:absolute top-12 z-10'>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>History</Link>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>Quality Policy</Link>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>Vision / Mission</Link>
       </ul>
      }

      <Link to='/' className='flex items-center text-slate-500 font-medium text-base px-3 py-3 nav-item hover:text-red-700 relative gap-1 '>Careers <FaCaretDown className='rotate'/></Link>
     {toggleState === 'careers' &&
      <ul className='grid pl-6 dropdown-item bg-red-100 lg:absolute top-12 z-10'>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>History</Link>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>Quality Policy</Link>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>Vision / Mission</Link>
       </ul>
      }

      <Link to='/' className='flex items-center text-slate-500 font-medium text-base px-3 py-3 nav-item hover:text-red-700 relative gap-1 '>Whats new <FaCaretDown className='rotate'/></Link>
     {toggleState === 'new' &&
      <ul className='grid pl-6 dropdown-item bg-red-100 lg:absolute top-12 z-10'>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>History</Link>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>Quality Policy</Link>
        <Link to='/' className='text-slate-500 hover:text-red-700 text-base'>Vision / Mission</Link>
       </ul>
      }

  )
}

and update using

const toggleDropdown = (id) => {
   setToggleState(prev=> {
       if(prev === id) {
         // close the opened dropdown
         return null;
       }     
       return id;
   })
} 

CodePudding user response:

Just to add to Shubham Khatri's answer, after writing

  const [toggleStates, setToggleStates] = useState({
       toggleDropDownAbout,
       toggleDropDownCareers,
       toggleDropDownNew
   });

you can destructure them to use them to avoid toggleStates.XX like so

const {toggleDropDownAbout, toggleDropDownCareers, toggleDropDownNew} = toggleStates;

  • Related