From 055177f53b0e3c0bc0ee440c5d0dd9509b01db52 Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Fri, 30 Jan 2026 22:58:30 +0000
Subject: [PATCH 1/2] =?UTF-8?q?=E2=9A=A1=20Optimize=20SearchInput=20with?=
=?UTF-8?q?=20React.memo?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Wrapped `src/components/SearchInput.js` with `React.memo` to prevent unnecessary re-renders.
- Refactored `src/App.js` to stabilize the `persist` function by moving it outside the component.
- Added a benchmark test `__tests__/SearchInputBenchmark.test.js` to verify the performance improvement (reduced render count from 2 to 1 on unrelated parent updates).
Co-authored-by: xRahul <1639945+xRahul@users.noreply.github.com>
---
__tests__/SearchInputBenchmark.test.js | 64 ++++++++++++++++++++++++++
src/App.js | 16 +++----
src/components/SearchInput.js | 6 +--
3 files changed, 75 insertions(+), 11 deletions(-)
create mode 100644 __tests__/SearchInputBenchmark.test.js
diff --git a/__tests__/SearchInputBenchmark.test.js b/__tests__/SearchInputBenchmark.test.js
new file mode 100644
index 0000000..9731d6b
--- /dev/null
+++ b/__tests__/SearchInputBenchmark.test.js
@@ -0,0 +1,64 @@
+import React, { useState } from 'react';
+import renderer, { act } from 'react-test-renderer';
+import SearchInput from '../src/components/SearchInput';
+import { TextInput } from 'react-native';
+
+const mockRenderSpy = jest.fn();
+
+// Mock react-native to work with react-test-renderer and spy on TextInput
+jest.mock('react-native', () => {
+ const ReactMock = require('react');
+ const TextInputMock = ReactMock.forwardRef((props, ref) => {
+ mockRenderSpy();
+ return ReactMock.createElement('TextInput', {...props, ref});
+ });
+ return {
+ TextInput: TextInputMock,
+ };
+});
+
+describe('SearchInput Re-render Benchmark', () => {
+ it('renders SearchInput only once when parent updates with stable props (optimized)', () => {
+ const persistMock = jest.fn();
+ const setSearchTextMock = jest.fn();
+
+ const Parent = () => {
+ const [count, setCount] = useState(0);
+
+ // We simulate a stable persist function here to isolate SearchInput's behavior
+ // independent of App.js implementation for now.
+ const stablePersist = persistMock;
+
+ return (
+
+
+ setCount(count + 1)} />
+
+ );
+ };
+
+ const component = renderer.create();
+
+ // Initial render
+ expect(mockRenderSpy).toHaveBeenCalledTimes(2); // SearchInput's TextInput + Updater TextInput
+
+ // Update state in Parent
+ const updater = component.root.findAllByType(TextInput)[1]; // The second one is the updater
+
+ mockRenderSpy.mockClear();
+
+ act(() => {
+ updater.props.onChangeText('update');
+ });
+
+ // With React.memo, SearchInput should NOT re-render because props are stable.
+ // SearchInput NO re-render.
+ // Updater TextInput re-renders.
+ // So we expect 1 render.
+ expect(mockRenderSpy).toHaveBeenCalledTimes(1);
+ });
+});
diff --git a/src/App.js b/src/App.js
index a5641d9..666c4ff 100644
--- a/src/App.js
+++ b/src/App.js
@@ -45,6 +45,14 @@ PushNotification.configure({
requestPermissions: true,
});
+const persist = async (key, value) => {
+ try {
+ await AsyncStorage.setItem(key, value);
+ } catch (error) {
+ console.log(error);
+ }
+};
+
const App = () => {
const [url, setUrl] = useState('');
const [searchText, setSearchText] = useState('');
@@ -114,14 +122,6 @@ const App = () => {
loadState();
}, []);
- const persist = async (key, value) => {
- try {
- await AsyncStorage.setItem(key, value);
- } catch (error) {
- console.log(error);
- }
- };
-
const createPrefetchJobs = async () => {
try {
setLoading(true);
diff --git a/src/components/SearchInput.js b/src/components/SearchInput.js
index 491c8d6..e2a2cb2 100644
--- a/src/components/SearchInput.js
+++ b/src/components/SearchInput.js
@@ -1,7 +1,7 @@
-import React, {forwardRef} from 'react';
+import React, {forwardRef, memo} from 'react';
import {TextInput} from 'react-native';
-const SearchInput = forwardRef(({searchText, setSearchText, persist}, ref) => {
+const SearchInput = memo(forwardRef(({searchText, setSearchText, persist}, ref) => {
return (
{
@@ -17,6 +17,6 @@ const SearchInput = forwardRef(({searchText, setSearchText, persist}, ref) => {
ref={ref}
/>
);
-});
+}));
export default SearchInput;
From bf6078957d654149048143da4b5757ae596a15f5 Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Fri, 30 Jan 2026 23:00:24 +0000
Subject: [PATCH 2/2] =?UTF-8?q?=E2=9A=A1=20Optimize=20SearchInput=20with?=
=?UTF-8?q?=20React.memo?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Wrapped `src/components/SearchInput.js` with `React.memo` to prevent unnecessary re-renders.
- Refactored `src/App.js` to stabilize the `persist` function by moving it outside the component.
- Added a benchmark test `__tests__/SearchInputBenchmark.test.js` to verify the performance improvement (reduced render count from 2 to 1 on unrelated parent updates).
- Fixed linting errors caught by CI.
Co-authored-by: xRahul <1639945+xRahul@users.noreply.github.com>
---
__tests__/SearchInputBenchmark.test.js | 13 ++++++----
src/components/SearchInput.js | 36 ++++++++++++++------------
2 files changed, 27 insertions(+), 22 deletions(-)
diff --git a/__tests__/SearchInputBenchmark.test.js b/__tests__/SearchInputBenchmark.test.js
index 9731d6b..e0db660 100644
--- a/__tests__/SearchInputBenchmark.test.js
+++ b/__tests__/SearchInputBenchmark.test.js
@@ -1,7 +1,7 @@
-import React, { useState } from 'react';
-import renderer, { act } from 'react-test-renderer';
+import React, {useState} from 'react';
+import renderer, {act} from 'react-test-renderer';
import SearchInput from '../src/components/SearchInput';
-import { TextInput } from 'react-native';
+import {TextInput} from 'react-native';
const mockRenderSpy = jest.fn();
@@ -36,7 +36,10 @@ describe('SearchInput Re-render Benchmark', () => {
setSearchText={setSearchTextMock}
persist={stablePersist}
/>
- setCount(count + 1)} />
+ setCount(count + 1)}
+ />
);
};
@@ -52,7 +55,7 @@ describe('SearchInput Re-render Benchmark', () => {
mockRenderSpy.mockClear();
act(() => {
- updater.props.onChangeText('update');
+ updater.props.onChangeText('update');
});
// With React.memo, SearchInput should NOT re-render because props are stable.
diff --git a/src/components/SearchInput.js b/src/components/SearchInput.js
index e2a2cb2..9dc67b3 100644
--- a/src/components/SearchInput.js
+++ b/src/components/SearchInput.js
@@ -1,22 +1,24 @@
import React, {forwardRef, memo} from 'react';
import {TextInput} from 'react-native';
-const SearchInput = memo(forwardRef(({searchText, setSearchText, persist}, ref) => {
- return (
- {
- setSearchText(text);
- }}
- onEndEditing={e => {
- persist('searchText', e.nativeEvent.text);
- }}
- value={searchText}
- autoCorrect={false}
- enablesReturnKeyAutomatically
- placeholder="Enter Search String"
- ref={ref}
- />
- );
-}));
+const SearchInput = memo(
+ forwardRef(({searchText, setSearchText, persist}, ref) => {
+ return (
+ {
+ setSearchText(text);
+ }}
+ onEndEditing={e => {
+ persist('searchText', e.nativeEvent.text);
+ }}
+ value={searchText}
+ autoCorrect={false}
+ enablesReturnKeyAutomatically
+ placeholder="Enter Search String"
+ ref={ref}
+ />
+ );
+ }),
+);
export default SearchInput;