From 75b360458424432c1f69db449bdda7b76d55c4db 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:06 +0000
Subject: [PATCH 1/2] Optimize UrlInput re-renders with React.memo and stable
callbacks
Co-authored-by: xRahul <1639945+xRahul@users.noreply.github.com>
---
__tests__/UrlInput_Performance.test.js | 79 ++++++++++++++++++++++++++
src/App.js | 14 +++--
src/components/UrlInput.js | 2 +-
3 files changed, 88 insertions(+), 7 deletions(-)
create mode 100644 __tests__/UrlInput_Performance.test.js
diff --git a/__tests__/UrlInput_Performance.test.js b/__tests__/UrlInput_Performance.test.js
new file mode 100644
index 0000000..7886aaa
--- /dev/null
+++ b/__tests__/UrlInput_Performance.test.js
@@ -0,0 +1,79 @@
+import React from 'react';
+import { create, act } from 'react-test-renderer';
+import UrlInput from '../src/components/UrlInput';
+import { Text, View } from 'react-native';
+
+const mockTextInputRender = jest.fn();
+
+// Mock React Native completely to avoid _interopRequireDefault error
+jest.mock('react-native', () => {
+ const React = require('react');
+ const TextInput = React.forwardRef((props, ref) => {
+ mockTextInputRender(props); // Spy on render
+ return React.createElement('TextInput', {...props, ref});
+ });
+
+ const View = (props) => React.createElement('View', props);
+ const Text = (props) => React.createElement('Text', props);
+
+ return {
+ TextInput,
+ View,
+ Text,
+ };
+});
+
+describe('UrlInput Performance', () => {
+ beforeEach(() => {
+ mockTextInputRender.mockClear();
+ });
+
+ it('renders unnecessarily when parent updates unrelated state', () => {
+ let setUnrelated;
+ const Parent = () => {
+ const [unrelated, setUnrelatedState] = React.useState(0);
+ setUnrelated = setUnrelatedState;
+
+ // Stable props
+ const noop = React.useCallback(() => {}, []);
+
+ return (
+
+
+ {unrelated}
+
+ );
+ };
+
+ let root;
+ act(() => {
+ root = create();
+ });
+
+ const initialRenderCount = mockTextInputRender.mock.calls.length;
+ console.log(`Initial Renders: ${initialRenderCount}`);
+
+ // Expect 1 render initially
+ expect(initialRenderCount).toBe(1);
+
+ act(() => {
+ setUnrelated(1);
+ });
+
+ const finalRenderCount = mockTextInputRender.mock.calls.length;
+ console.log(`Final Renders: ${finalRenderCount}`);
+
+ // With optimization, finalRenderCount should be 1.
+ // Without optimization, it should be 2.
+ // We want this test to FAIL if optimization is missing (to verify baseline).
+ // So we assert the OPTIMIZED behavior.
+
+ // NOTE: This assertion will fail now (Expected 1, Received 2).
+ expect(finalRenderCount).toBe(initialRenderCount);
+ });
+});
diff --git a/src/App.js b/src/App.js
index a5641d9..965ee51 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,4 +1,4 @@
-import React, {useState, useEffect, useRef} from 'react';
+import React, {useState, useEffect, useRef, useCallback} from 'react';
import {
Platform,
Text,
@@ -114,13 +114,17 @@ const App = () => {
loadState();
}, []);
- const persist = async (key, value) => {
+ const persist = useCallback(async (key, value) => {
try {
await AsyncStorage.setItem(key, value);
} catch (error) {
console.log(error);
}
- };
+ }, []);
+
+ const handleUrlSubmit = useCallback(() => {
+ searchTextInputRef.current && searchTextInputRef.current.focus();
+ }, []);
const createPrefetchJobs = async () => {
try {
@@ -202,9 +206,7 @@ const App = () => {
url={url}
setUrl={setUrl}
persist={persist}
- onSubmitEditing={() =>
- searchTextInputRef.current && searchTextInputRef.current.focus()
- }
+ onSubmitEditing={handleUrlSubmit}
/>
{
);
};
-export default UrlInput;
+export default React.memo(UrlInput);
From b9ed53258123aff4cc2586b1c02d37e30bc331c1 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:04:40 +0000
Subject: [PATCH 2/2] Fix lint errors in UrlInput_Performance test
Co-authored-by: xRahul <1639945+xRahul@users.noreply.github.com>
---
__tests__/UrlInput_Performance.test.js | 21 ++++++++++-----------
1 file changed, 10 insertions(+), 11 deletions(-)
diff --git a/__tests__/UrlInput_Performance.test.js b/__tests__/UrlInput_Performance.test.js
index 7886aaa..17be2ae 100644
--- a/__tests__/UrlInput_Performance.test.js
+++ b/__tests__/UrlInput_Performance.test.js
@@ -1,25 +1,25 @@
import React from 'react';
-import { create, act } from 'react-test-renderer';
+import {create, act} from 'react-test-renderer';
+import {Text, View} from 'react-native';
import UrlInput from '../src/components/UrlInput';
-import { Text, View } from 'react-native';
const mockTextInputRender = jest.fn();
// Mock React Native completely to avoid _interopRequireDefault error
jest.mock('react-native', () => {
- const React = require('react');
- const TextInput = React.forwardRef((props, ref) => {
+ const RNReact = require('react');
+ const TextInput = RNReact.forwardRef((props, ref) => {
mockTextInputRender(props); // Spy on render
- return React.createElement('TextInput', {...props, ref});
+ return RNReact.createElement('TextInput', {...props, ref});
});
- const View = (props) => React.createElement('View', props);
- const Text = (props) => React.createElement('Text', props);
+ const RNView = props => RNReact.createElement('View', props);
+ const RNText = props => RNReact.createElement('Text', props);
return {
TextInput,
- View,
- Text,
+ View: RNView,
+ Text: RNText,
};
});
@@ -50,9 +50,8 @@ describe('UrlInput Performance', () => {
);
};
- let root;
act(() => {
- root = create();
+ create();
});
const initialRenderCount = mockTextInputRender.mock.calls.length;