diff --git a/__tests__/SearchInputBenchmark.test.js b/__tests__/SearchInputBenchmark.test.js
new file mode 100644
index 0000000..e0db660
--- /dev/null
+++ b/__tests__/SearchInputBenchmark.test.js
@@ -0,0 +1,67 @@
+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..9dc67b3 100644
--- a/src/components/SearchInput.js
+++ b/src/components/SearchInput.js
@@ -1,22 +1,24 @@
-import React, {forwardRef} from 'react';
+import React, {forwardRef, memo} from 'react';
import {TextInput} from 'react-native';
-const SearchInput = 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;