From 1f337f082fc5501df89c53d6d22d4c3e8bf0b0d0 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 11:03:59 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Optimize=20derived=20state?= =?UTF-8?q?=20calculation=20in=20notifications?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaced unnecessary `useState` + `useEffect` chains with `useMemo` in `NotificationMenu.tsx` and `NotificationTabPanel.tsx`. This optimization prevents double-render cycles and reduces CPU overhead when calculating derived state. Co-authored-by: sshahriazz <34005640+sshahriazz@users.noreply.github.com> --- .jules/bolt.md | 3 +++ .../notification/NotificationTabPanel.tsx | 19 +++++++++---------- .../main-layout/common/NotificationMenu.tsx | 18 +++++++++--------- 3 files changed, 21 insertions(+), 19 deletions(-) create mode 100644 .jules/bolt.md diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..150f5ea --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2024-03-23 - Memoize Derived State to Prevent Extra Renders +**Learning:** Using `useEffect` to map or reduce props into local `useState` causes an unnecessary double-render cycle in React. In `NotificationMenu` and `NotificationTabPanel`, parsing `notificationsData` into `today` and `older` buckets on mount or update triggered this pattern, unnecessarily blocking paint and wasting CPU cycles. +**Action:** Replace `useState` + `useEffect` chains for derived state with `useMemo`. This allows the derived calculation to run synchronously during the render phase only when dependencies (`notificationsData`) change, saving an entire render pass. diff --git a/client/src/components/sections/notification/NotificationTabPanel.tsx b/client/src/components/sections/notification/NotificationTabPanel.tsx index 47e5002..44e04a2 100644 --- a/client/src/components/sections/notification/NotificationTabPanel.tsx +++ b/client/src/components/sections/notification/NotificationTabPanel.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useMemo } from 'react'; import TabPanel from '@mui/lab/TabPanel'; import { Button, Divider, dividerClasses, listItemClasses } from '@mui/material'; import dayjs from 'dayjs'; @@ -11,13 +11,14 @@ interface NotificationTabPanelProps { } const NotificationTabPanel = ({ value, notificationsData }: NotificationTabPanelProps) => { - const [notifications, setNotifications] = useState({ - today: [], - older: [], - }); - - useEffect(() => { - const datewiseNotification = notificationsData.reduce( + /** + * ⚡ Bolt Performance Optimization: + * Replaced `useEffect` + `useState` derived state calculation with `useMemo`. + * This removes an unnecessary extra render cycle whenever `notificationsData` changes, + * avoiding layout shifts and reducing CPU overhead during component mounting/updating. + */ + const notifications = useMemo(() => { + return notificationsData.reduce( (acc: DatewiseNotification, val) => { if (dayjs().diff(dayjs(val.createdAt), 'days') === 0) { acc.today.push(val); @@ -31,8 +32,6 @@ const NotificationTabPanel = ({ value, notificationsData }: NotificationTabPanel older: [], }, ); - - setNotifications(datewiseNotification); }, [notificationsData]); return ( diff --git a/client/src/layouts/main-layout/common/NotificationMenu.tsx b/client/src/layouts/main-layout/common/NotificationMenu.tsx index 6db7737..1ba4d61 100644 --- a/client/src/layouts/main-layout/common/NotificationMenu.tsx +++ b/client/src/layouts/main-layout/common/NotificationMenu.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useEffect, useState } from 'react'; +import { useMemo, useState } from 'react'; import { badgeClasses, Box, Button, Link, paperClasses, Popover, Stack } from '@mui/material'; import { notifications as notificationsData } from 'data/notifications'; import dayjs from 'dayjs'; @@ -17,10 +17,6 @@ interface NotificationMenuProps { } const NotificationMenu = ({ type = 'default' }: NotificationMenuProps) => { - const [notifications, setNotifications] = useState({ - today: [], - older: [], - }); const [anchorEl, setAnchorEl] = useState(null); const { @@ -35,8 +31,14 @@ const NotificationMenu = ({ type = 'default' }: NotificationMenuProps) => { setAnchorEl(null); }; - useEffect(() => { - const datewiseNotification = notificationsData.reduce( + /** + * ⚡ Bolt Performance Optimization: + * Replaced `useEffect` + `useState` derived state calculation with `useMemo`. + * This removes an unnecessary extra render cycle whenever `notificationsData` changes, + * avoiding layout shifts and reducing CPU overhead during component mounting/updating. + */ + const notifications = useMemo(() => { + return notificationsData.reduce( (acc: DatewiseNotification, val) => { if (dayjs().diff(dayjs(val.createdAt), 'days') === 0) { acc.today.push(val); @@ -50,8 +52,6 @@ const NotificationMenu = ({ type = 'default' }: NotificationMenuProps) => { older: [], }, ); - - setNotifications(datewiseNotification); }, [notificationsData]); return (