I am building a react native app for which has a form page contains a lot of textInput and buttons. Each of fields do not causing performance issue.
However, when they are put together, the application will show js thread frame drops in the Perf Monitor. So I think the page is re-rendering all the components when any of the state is changed.
I tried 2 methods.
- Moving components out of the main export function.
- Using
React.memo
.
Unfortunately, both of them are not working. The app still lag and re-render every time for all components.
So I created a simple case for it. You may see the code below.
I would like to only re-render the ChildItem
for only the respective state changed. For now, console will log from 1 to 5 for any button clicked. Since the props
passed to memo component is not changed, I expect the console will only log 1 respective sentence for specific index when button is pressed.
import React, {useState, memo} from 'react';
import { View, Text, Button } from 'react-native';
const ChildeItem = memo( ({index, value, onPress}) =>{
console.log(`ChildeItem ${index} rendered`);
return(
<View style={{flexDirection: 'row'}}>
<Text style={{flex: 1, textAlign: 'center'}}>
{value}
</Text>
<View style={{flex: 1}}>
<Button
onPress={onPress}
title={" "}
/>
</View>
</View>
)
});
export default function TestScreen({navigation}) {
const [value1, setValue1] = useState(0);
const [value2, setValue2] = useState(0);
const [value3, setValue3] = useState(0);
const [value4, setValue4] = useState(0);
const [value5, setValue5] = useState(0);
return(
<View>
<ChildeItem index={1} value={value1} onPress={()=>{setValue1(value1 1);}} />
<ChildeItem index={2} value={value2} onPress={()=>{setValue2(value2 1);}} />
<ChildeItem index={3} value={value3} onPress={()=>{setValue3(value3 1);}} />
<ChildeItem index={4} value={value4} onPress={()=>{setValue4(value4 1);}} />
<ChildeItem index={5} value={value5} onPress={()=>{setValue5(value5 1);}} />
</View>
)
}
Or I have any misunderstanding for React.memo
? Any help will be appreciated. Thanks.
CodePudding user response:
Your index
and value
props are ok for memoization, but your onPress
prop causes re-render because on every render of the TestScreen, your onPress prop functions are re-created, so it's reference changes. You can prevent this by using useCallback
hook.
export default function TestScreen({navigation}) {
const [value1, setValue1] = useState(0);
const [value2, setValue2] = useState(0);
const [value3, setValue3] = useState(0);
const [value4, setValue4] = useState(0);
const [value5, setValue5] = useState(0);
// Use useCallback for each function
const onPress1 = useCallback(() => setValue1(value1 1), [value1]);
const onPress2 = useCallback(() => setValue2(value2 1), [value2]);
const onPress3 = useCallback(() => setValue3(value3 1), [value3]);
const onPress4 = useCallback(() => setValue4(value4 1), [value4]);
const onPress5 = useCallback(() => setValue5(value5 1), [value5]);
return(
<View>
<ChildeItem index={1} value={value1} onPress={onPress1} />
<ChildeItem index={2} value={value2} onPress={onPress2} />
<ChildeItem index={3} value={value3} onPress={onPress3} />
<ChildeItem index={4} value={value4} onPress={onPress4} />
<ChildeItem index={5} value={value5} onPress={onPress5} />
</View>
)
}
By using useCallback
hook, you're memoizing the onPress functions, so they are re-created only when the relevant value changes.