Search code examples
reactjsdatenestedstate

How to update deep nested state in React


[
{  
year:2022, 
month:"jan",
days: [
            { day: 1, isCompleted: false },
            { day: 2, isCompleted: false },
            // ... other days
      ]
},
{  
year:2022, 
month:"feb",
days: [
            { day: 1, isCompleted: false },
            { day: 2, isCompleted: false },
            // ... other days
      ]
},
// ... other months
]

My state value are like that. I render my all days and if i click specific day, i want to update it property of isCompleted to true. How can i update it.

I cannot figure out how to update nested state


Solution

  • You need to look for the year, month and day. Then just set isCompleted value to true. It might be a bit complicated, because when setting a state you must not modify the state itself.

    const SomeComponent = () => {
      const [datesState, setDatesState] = useState([...]);
    
      const onSelectDate = (inputDate) => {
        const inputDateDay = inputDate.getDate();
        const inputDateMonth = inputDate.toLocaleString("en-US", { month: "short" }).toLowerCase();
        const inputDateYear = inputDate.getFullYear();
    
        setDatesState((prev) => {
          const matchingYearAndMonthIndex = datesState.findIndex((date) => date.year === inputDateYear && date.month === inputDateMonth);
    
          if (matchingYearAndMonthIndex === -1) {
            return prev;
          }
    
          const matchingDayIndex = prev[matchingYearAndMonthIndex].findIndex((date) => date.day === inputDateDay);
    
          if (matchingDayIndex === -1) {
            return prev;
          }
    
          const prevCopy = [...prev];
          const yearAndMonthCopy = { ...prev[matchingYearAndMonthIndex] };
          const daysCopy = [ ...yearAndMonthCopy.days ];
    
          daysCopy[matchingDayIndex].isCompleted = true;
          yearAndMonthCopy.days = daysCopy;
          prevCopy[matchingYearAndMonthIndex] = yearAndMonthCopy;
    
          return prevCopy;
        });
      }
    }