From 2b153fea613981247248d7d115354eb403de1b4d Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 18 Feb 2026 10:08:58 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Optimize=20React=20renderin?= =?UTF-8?q?g=20in=20Chat=20and=20Map=20components?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR implements two performance optimizations: 1. Memoized `MapDataProvider` context value in `components/map/map-data-context.tsx` to prevent unnecessary re-renders of map data consumers when the provider's parent re-renders. 2. Replaced `showEmptyScreen` state/effect with a derived variable in `components/chat.tsx` to eliminate an extra render cycle and reduce state complexity. These changes improve overall app responsiveness, especially during map interactions and message updates. Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com> --- .jules/bolt.md | 3 +++ components/chat.tsx | 6 ++---- components/map/map-data-context.tsx | 7 +++++-- 3 files changed, 10 insertions(+), 6 deletions(-) create mode 100644 .jules/bolt.md diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 00000000..2d62013f --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2026-02-18 - [React Performance: Context Memoization & Derived Variables] +**Learning:** React Context providers that pass object literals as values cause all consumers to re-render whenever the provider re-renders, even if the data hasn't changed. Similarly, using `useState` + `useEffect` for simple UI logic (like `showEmptyScreen`) introduces an extra render cycle and unnecessary state management overhead. +**Action:** Always memoize Context provider values using `useMemo` and prefer derived variables over `useState` for simple data transformations from existing state/props. diff --git a/components/chat.tsx b/components/chat.tsx index e675f124..d31f8cd5 100644 --- a/components/chat.tsx +++ b/components/chat.tsx @@ -36,7 +36,6 @@ export function Chat({ id }: ChatProps) { const { isUsageOpen } = useUsageToggle(); const { isCalendarOpen } = useCalendarToggle() const [input, setInput] = useState('') - const [showEmptyScreen, setShowEmptyScreen] = useState(false) const [isSubmitting, setIsSubmitting] = useState(false) const [suggestions, setSuggestions] = useState(null) const chatPanelRef = useRef(null); @@ -49,9 +48,8 @@ export function Chat({ id }: ChatProps) { chatPanelRef.current?.submitForm(); }; - useEffect(() => { - setShowEmptyScreen(messages.length === 0) - }, [messages]) + // Optimization: Use a derived variable instead of state to avoid unnecessary render cycles + const showEmptyScreen = messages.length === 0 useEffect(() => { // Check if device is mobile diff --git a/components/map/map-data-context.tsx b/components/map/map-data-context.tsx index 9b102547..72800b69 100644 --- a/components/map/map-data-context.tsx +++ b/components/map/map-data-context.tsx @@ -1,6 +1,6 @@ 'use client'; -import React, { createContext, useContext, useState, ReactNode } from 'react'; +import React, { createContext, useContext, useState, ReactNode, useMemo } from 'react'; // Define the shape of the map data you want to share export interface CameraState { center: { lat: number; lng: number }; @@ -41,8 +41,11 @@ const MapDataContext = createContext(undefined); export const MapDataProvider: React.FC<{ children: ReactNode }> = ({ children }) => { const [mapData, setMapData] = useState({ drawnFeatures: [], markers: [] }); + // Memoize the context value to prevent unnecessary re-renders of consumers + const value = useMemo(() => ({ mapData, setMapData }), [mapData]); + return ( - + {children} );