Home > database >  Dates stored in states not updating/rendering properly
Dates stored in states not updating/rendering properly

Time:06-24

I am trying to make a planner, which has buttons to switch months. I am using react and states to store dates. The individual months are being mapped properly, but switching between months gives some strange behaviour. The months update, but the dates don't seem to be being updated and the button has to be pressed multiple times for the map to update. Pressing the other button then jumps two months and the same behaviour occurs.

const MonthView = () => {

    const months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
    const daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    const daysOfWeek = ["Sun", "Mon", "Tues", "Wed", "Thu", "Fri", "Sat"];

    const globalDate = new Date();
    // ^ switches between two months. strange behaviour. fix?
    // date does not appear to actially change.
    const [month, setMonth] = useState(globalDate.getMonth());
    const [year, setYear] = useState(globalDate.getFullYear());
    const [day, setDay] = useState(globalDate.getDate());
    const [dayOfWeek, setDayOfWeek] = useState(globalDate.getDay());
    const [days, setDays] = useState([]);
    const [events, setEvents] = useState([]);
    const [firstDayOfMonthDay, setFirstDayOfMonthDay] = useState(daysOfWeek[new Date(year, month, 1).getDay()]);
    const [firstDayOfMonth, setFirstDayOfMonth] = useState(new Date(year, month, 1).getDay());
    const [lastDayOfMonth, setLastDayOfMonth] = useState(new Date(year, month   1, 0).getDay());
    const [lastDayOfMonthNumber, setLastDayOfMonthNumber] = useState(new Date(year, month   1, 0).getDate());
    const [firstDayOfMonthNumber, setFirstDayOfMonthNumber] = useState(new Date(year, month, 1).getDate());

    const updateStates = () => {
        console.log("gdate: "   globalDate);
        setMonth(globalDate.getMonth());
        setYear(globalDate.getFullYear());
        setDay(globalDate.getDate());
        setDayOfWeek(globalDate.getDay());
        setFirstDayOfMonthDay(daysOfWeek[new Date(year, month, 1).getDay()]);
        setFirstDayOfMonth(new Date(year, month, 1).getDay());
        setLastDayOfMonth(new Date(year, month   1, 0).getDay());
        setLastDayOfMonthNumber(new Date(year, month   1, 0).getDate());
        setFirstDayOfMonthNumber(new Date(year, month, 1).getDate());
        set_days();
    }

    const set_days = () => {
        let days = [];
        for (let i = 0; i < firstDayOfMonth; i  ) {
            days.push(null);
        }
        for (let i = 1; i <= lastDayOfMonthNumber; i  ) {
            days.push(i);
        }
        for (let i = 0; i < (6 - lastDayOfMonth); i  ) {
            days.push(i 1);
        }
        setDays(days);
        console.log(days)
        console.log(globalDate);
    }
        


    useEffect(() => {
        set_days();
    }, []);

    return(
        <>
        <Table striped bordered hover size="sm" variant="light">
            <thead>
                <tr>
                    <th></th>
                    <th></th>
                    <th><Button variant="info" onClick={() => {globalDate.setMonth(globalDate.getMonth() - 1); updateStates();}}><FontAwesomeIcon icon={faAngleLeft} /></Button></th>
                    <th>{months[month]}</th>
                    <th><Button variant="info" onClick={() => {globalDate.setMonth(globalDate.getMonth()   1); updateStates();}}><FontAwesomeIcon icon={faAngleRight} /></Button></th>
                    <th></th>
                    <th></th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    {daysOfWeek.map((day, index) => {
                        return <td key={index}>{day}</td>
                    }
                    )}
                </tr>
            
                {days.map((day, index) => {
                    if (index % 7 === 5) {
                        // MAKE CLICK ON CELL RETURN VAL and create new event on that day
                        // also week view and day view
                        // use lighthouse to test and do everything possible to decrease load time
                        return <tr key={index}> {days.slice(index - 5, index   2).map((day, index2) => {
                            return <td key={index2} onClick={() => {console.log(index   index2 - 5);}}>{day}</td>
                        }
                        )}
                        </tr>
                    }
                   
                }
                )}

            </tbody>
        </Table>
        </>
    )
}

CodePudding user response:

As commenter stefan mentioned, we can greatly simplify this code to reduce the possibility of errors. You have a lot of state variables that are calculated from globalDate. If you can always calculate a value from other state, you should not store it as state. You can simply declare them as constants. You no longer need your updateStates logic, and your set_days function doesn't need to be in a useEffect hook. After removing redundant state, the only state variables you really need are globalDate and events.

Another issue is your button onClick behavior. When you call globalDate.setMonth(), you are mutating state. State in React is immutable and should be updated by creating a new copy of state and changing the parts you need. To remedy this, you can instantiate a new Date object, call setMonth on that object, then set globalDate to that new object.

The following simplifies your code and fixes the issue you are experiencing:

  const MonthView = () => {
    const months = [
      "January",
      "February",
      "March",
      "April",
      "May",
      "June",
      "July",
      "August",
      "September",
      "October",
      "November",
      "December"
    ];
    const daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    const daysOfWeek = ["Sun", "Mon", "Tues", "Wed", "Thu", "Fri", "Sat"];

    const [events, setEvents] = useState([]);
    const [globalDate, setGlobalDate] = useState(new Date());

    const month = globalDate.getMonth();
    const year = globalDate.getFullYear();
    const day = globalDate.getDate();
    const dayOfWeek = globalDate.getDay();
    const firstDayOfMonthDay = daysOfWeek[new Date(year, month, 1).getDay()];
    const firstDayOfMonth = new Date(year, month, 1).getDay();
    const lastDayOfMonth = new Date(year, month   1, 0).getDay();
    const lastDayOfMonthNumber = new Date(year, month   1, 0).getDate();
    const firstDayOfMonthNumber = new Date(year, month, 1).getDate();
    const days = (() => {
      let daysArr = [];
      for (let i = 0; i < firstDayOfMonth; i  ) {
        daysArr.push(null);
      }
      for (let i = 1; i <= lastDayOfMonthNumber; i  ) {
        daysArr.push(i);
      }
      for (let i = 0; i < 6 - lastDayOfMonth; i  ) {
        daysArr.push(i   1);
      }
      return daysArr;
    })();

    return (
      <>
        <Table>
          <thead>
            <tr>
              <th></th>
              <th></th>
              <th>
                <Button
                  variant="info"
                  onClick={() => {
                    const newDate = new Date();
                    newDate.setMonth(globalDate.getMonth() - 1);
                    setGlobalDate(newDate);
                  }}
                >
                  <FontAwesomeIcon icon={faAngleLeft} />
                </Button>
              </th>
              <th>{months[month]}</th>
              <th>
                <Button
                  variant="info"
                  onClick={() => {
                    const newDate = new Date();
                    newDate.setMonth(globalDate.getMonth()   1);
                    setGlobalDate(newDate);
                  }}
                >
                  <FontAwesomeIcon icon={faAngleRight} />
                </Button>
              </th>
              <th></th>
              <th></th>
            </tr>
          </thead>
          <tbody>
            <tr>
              {daysOfWeek.map((day, index) => {
                return <td key={index}>{day}</td>;
              })}
            </tr>

            {days.map((day, index) => {
              if (index % 7 === 5) {
                // MAKE CLICK ON CELL RETURN VAL and create new event on that day
                // also week view and day view
                // use lighthouse to test and do everything possible to decrease load time
                return (
                  <tr key={index}>
                    {" "}
                    {days.slice(index - 5, index   2).map((day, index2) => {
                      return (
                        <td
                          key={index2}
                          onClick={() => {
                            console.log(index   index2 - 5);
                          }}
                        >
                          {day}
                        </td>
                      );
                    })}
                  </tr>
                );
              }
            })}
          </tbody>
        </Table>
      </>
    );
  };

You have some unused variables that I left in just in case you need them later. It looks like some values like firstDayOfMonth, lastDayOfMonth, and lastDayOfMonthNumber are used only in the function that generates the days array, so they can probably be moved into the body of that function if they aren't needed elsewhere.

  • Related