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
It only creates a
redux store
, but doesn’t have areact
andredux
connection.You need a subscription to use the
redux store
withreact
.Once subscribed,
react
will render whenever the value in theredux store
changes.Use the hooks provided by
redux
to connectreact
andredux
.Calling
store.getState()
doesn’t subscribe a React component to the store, it’s a one-and-done action. Use theuseSelector
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:
Suggestion: The
HabitTrackerWeekView
should use theuseSelector
hook and select the state it needs instead of having them selected in a parent component and passed as props.