skip to Main Content

I’m experiencing issues with re-renders in my React component when using a Redux store.
I am using functional component.

here is my App.js which should be re-render

import Habbit from "./Habbit";
import NavBar from "./NavBar";
import HabitTrackerWeekView from "./HabitTrackerWeekView";
import { switchComponents, addHabit } from "../Action";
import { useEffect, useState } from "react";

function App({ store }) {
  const [newHabit, setNewHabit] = useState("");
  const { habitList, switchCom, currentMonth, doneCount } = store.getState();
  useEffect(() => {
    console.log("app.js done count ", doneCount);
  }, [doneCount]);


  return (
    <div className="App">
      <NavBar />
      <HabitTrackerWeekView
        habits={store.getState()}
        dispatch={store.dispatch}
        currentMonth={currentMonth}
      />
    </div>
  );
}

export default App;

here is my HabitTrackerWeekView.js where doneCount sending to the redux store

import React, { useEffect, useReducer, useState } from "react";
import { countDoneStatuses } from "../Action";
import {
  format,
  subWeeks,
  startOfWeek,
  endOfWeek,
  eachDayOfInterval,
  addWeeks,
} from "date-fns";

function HabitTrackerWeekView({ habits, dispatch }) {
  const { habitList } = habits;
  const [currentMonth, setCurrentMonth] = useState(new Date());
  const [habitStatuses, setHabitStatuses] = useState({});
  const [habitDoneCounts, setHabitDoneCounts] = useState(
    habitList.reduce((counts, habit) => ({ ...counts, [habit]: 0 }), {})
  );
  useEffect(() => {
    // console.log("Current habitDoneCounts:", habitDoneCounts);
    dispatch(countDoneStatuses(habitDoneCounts));
  }, [habitDoneCounts]);

  const weekdays = [
    "Sunday",
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday",
    "Saturday",
  ];
  const getCurrentWeekDates = () => {
    const startOfCurrentWeek = startOfWeek(currentMonth, { weekStartsOn: 1 });
    const endOfCurrentWeek = endOfWeek(currentMonth, { weekStartsOn: 1 });
    const datesOfCurrentWeek = eachDayOfInterval({
      start: startOfCurrentWeek,
      end: endOfCurrentWeek,
    });
    const formattedDatesOfCurrentWeek = datesOfCurrentWeek.map((date) =>
      format(date, "dd")
    );

    return formattedDatesOfCurrentWeek;
  };
  const dates = getCurrentWeekDates();
  const handlePreviousWeek = () => {
    setCurrentMonth(subWeeks(currentMonth, 1));
  };

  const getMonthYear = () => {
    const dateFormat = "MMM yyyy";
    return format(currentMonth, dateFormat);
  };

  const handleNextWeek = () => {
    setCurrentMonth(addWeeks(currentMonth, 1));
  };

  const handleTodaysStatus = (date, habit) => {
    const key = `${date}-${habit}`;
    setHabitStatuses((prevStatuses) => {
      const currentStatus = prevStatuses[key] || "none";

      const newStatus =
        currentStatus === "none"
          ? "done"
          : currentStatus === "done"
          ? "not done"
          : "none";
      setHabitDoneCounts((prevCounts) => ({
      ...prevCounts,
      [habit]:
        newStatus === "done"
          ? prevCounts[habit] + 1
          : Math.max(0, prevCounts[habit] - 1),
    }));
      return { ...prevStatuses, [key]: newStatus };
    });
    // console.log(habitStatuses);
  };

  return (
    <div className="habit-tracker-week-view">
      <div className="months">
        <div className="previous-month" onClick={handlePreviousWeek}>
          <img
            width="24"
            height="24"
            src="https://img.icons8.com/material-outlined/24/back--v1.png"
            alt="back--v1"
          />
        </div>
        <div className="month">{getMonthYear()}</div>
        <div className="next-month" onClick={handleNextWeek}>
          <img
            width="24"
            height="24"
            src="https://img.icons8.com/material-outlined/24/forward.png"
            alt="forward"
          />
        </div>
      </div>
      <div className="day-of-the-week">
        {weekdays.map((day) => (
          <label>{day}</label>
        ))}
      </div>
      {/* <h1>Habit Tracker</h1> */}
      <div className="habits">
        {habitList.map((habit) => (
          <>
            <div className="habit-info">
              <div className="habit-name">{habit}</div>
              <div className="habit-time">time</div>
            </div>
            <div className="habit-dates">
              {dates.map((date) => (
                <div
                  className={`habit-date ${
                    habitStatuses[`${date}-${habit}`] === "done"
                      ? "done"
                      : `${
                          habitStatuses[`${date}-${habit}`] === "not done"
                            ? "not-done"
                            : ""
                        }`
                  }`}
                  onClick={() => handleTodaysStatus(date, habit)}
                >
                  {date}
                </div>
              ))}
            </div>
          </>
        ))}
      </div>
    </div>
  );
}
export default HabitTrackerWeekView;

I checked on redux-dev-tool the flow is is good and it is updating the correct state

2

Answers


  1. It only creates a redux store, but doesn’t have a react and redux connection.
    You need a subscription to use the redux store with react.
    Once subscribed, react will render whenever the value in the redux store changes.

    Use the hooks provided by redux to connect react and redux.

    import { useSelector } from 'react-redux';
    ...
    const store = useSelector(state => state);
    
    
    Login or Signup to reply.
  2. Calling store.getState() doesn’t subscribe a React component to the store, it’s a one-and-done action. Use the useSelector hook to subscribe to changes in the store. When the selected values change, the component is triggered to rerender and selects the updated state values.

    Example:

    ...
    import { useDispatch, useSelector } from 'react-redux';
    ...
    
    function App() {
      const dispatch = useDispatch();
    
      const habitList = useSelector(state => state.habitList);
      const switchCom = useSelector(state => state.switchCom);
      const currentMonth = useSelector(state => state.currentMonth);
      const doneCount = useSelector(state => state.doneCount);
    
      const [newHabit, setNewHabit] = useState("");
      
      useEffect(() => {
        console.log("app.js done count ", doneCount);
      }, [doneCount]);
    
    
      return (
        <div className="App">
          <NavBar />
          <HabitTrackerWeekView
            habits={habitList}
            dispatch={dispatch}
            currentMonth={currentMonth}
          />
        </div>
      );
    }
    

    Suggestion: The HabitTrackerWeekView should use the useSelector hook and select the state it needs instead of having them selected in a parent component and passed as props.

    function HabitTrackerWeekView() {
      const dispatch = useDispatch();
    
      const habitList = useSelector(state => state.habitList);
      const currentMonth = useSelector(state => state.currentMonth);
      const doneCount = useSelector(state => state.doneCount);
    
      ...
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search