React Native App Performance
Ajmal Hasan
Posted on October 25, 2022
React useCallback Hook
The React useCallback Hook returns a memoized callback function.
Think of memoization as caching a value so that it does not need to be recalculated.
This allows us to isolate resource intensive functions so that they will not automatically run on every render.
The useCallback Hook only runs when one of its dependencies update.
This can improve performance.
The useCallback and useMemo Hooks are similar. The main difference is that useMemo returns a memoized value and useCallback returns a memoized function.
One reason to use useCallback is to prevent a component from re-rendering unless its props have changed.
EXAMPLE WITH EXPLANATORY COMMENT:
import {StyleSheet, Text, TouchableOpacity, View} from 'react-native';
import React, {useCallback, useState} from 'react';
const App = () => {
const [number, setnumber] = useState(0);
// 1. Whenever set state is called all function that are not memoized get recreated
// const onDecrement = () => {
// console.log('rerenders-', number);
// setnumber(number - 1);
// };
// const onIncrement = () => {
// console.log('rerenders+', number);
// setnumber(number + 1);
// };
// 2. If we dont pass dependency array next state updation wont happen and reflect, as this function does not get recreated
// const onDecrement = useCallback(() => {
// console.log('rerenders-', number);
// setnumber(number - 1);
// }, []);
// 3. Since we have memoised the function and also passed dependency so this function will only be recreated whenever dependency data changes
const onDecrement = useCallback(() => {
console.log('rerenders-', number);
setnumber(number - 1);
}, [number]);
const onIncrement = useCallback(() => {
console.log('rerenders+', number);
setnumber(number + 1);
}, [number]);
// 4. Since below function is not memoised this function will be recreated whenever any state changes
const notMemoizedFunction = () => {
console.log('rerendersNM', number);
};
return (
<View style={styles.container}>
<View style={{alignContent: 'center', alignItems: 'center'}}>
<TouchableOpacity onPress={onDecrement}>
<Text style={styles.textStyle}>-</Text>
</TouchableOpacity>
<Text style={styles.textStyle}>{number}</Text>
<TouchableOpacity onPress={onIncrement}>
<Text style={styles.textStyle}>+</Text>
</TouchableOpacity>
</View>
</View>
);
};
export default App;
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
textStyle: {
fontSize: 20,
fontWeight: 'bold',
},
alignCenter: {
alignContent: 'center',
alignItems: 'center',
},
});
React Memo
Using memo will cause React to skip rendering a component if its props have not changed.
This can improve performance.
EXAMPLE WITH EXPLANATORY COMMENT:
index.js
import {StyleSheet, Text, TouchableOpacity, View} from 'react-native';
import React, {useCallback, useState} from 'react';
import CounterComponent from './CounterComponent';
const App = () => {
const [number, setnumber] = useState(0);
const [randomNumber, setrandomNumber] = useState(0);
const onDecrement = useCallback(() => {
setnumber(number - 1);
}, [number]);
const onIncrement = useCallback(() => {
setnumber(number + 1);
}, [number]);
// 1. Since randomNumber will always be random, so no need to add dependency as always different value is memoised
// 2. On clicking onRandomNumber, child component ie "CounterComponent" also gets rerendered though we are not passing randomNumber state to it.
// 3. In order to avoid it we wrap our child component with React.memo(ChildComponent), so that this component only gets rerendered when passed prop changes ie number.
const onRandomNumber = useCallback(() => {
setrandomNumber(Math.random());
}, []);
return (
<View style={styles.container}>
<View style={{alignContent: 'center', alignItems: 'center'}}>
<Text onPress={onRandomNumber} style={styles.textStyle}>
Random Number {randomNumber}
</Text>
<TouchableOpacity onPress={onDecrement}>
<Text style={styles.textStyle}>-</Text>
</TouchableOpacity>
<CounterComponent number={number} />
<TouchableOpacity onPress={onIncrement}>
<Text style={styles.textStyle}>+</Text>
</TouchableOpacity>
</View>
</View>
);
};
export default App;
CounterComponent.js
import {StyleSheet, Text, View} from 'react-native';
import React from 'react';
const CounterComponent = ({number = 0}) => {
console.log('Is component rerendered', number);
return <Text style={styles.textStyle}>{number}</Text>;
};
export default React.memo(CounterComponent);
React useMemo Hook
The React useMemo Hook returns a memoized value.
Think of memoization as caching a value so that it does not need to be recalculated.
The useMemo Hook only runs when one of its dependencies update.
This can improve performance.
The useMemo and useCallback Hooks are similar. The main difference is that useMemo returns a memoized value and useCallback returns a memoized function.
The useMemo Hook can be used to keep expensive, resource intensive functions from needlessly running.
EXAMPLE WITH EXPLANATORY COMMENT:
import {StyleSheet, Text, TouchableOpacity, View} from 'react-native';
import React, {useCallback, useMemo, useState} from 'react';
const App = () => {
const [number, setnumber] = useState(0);
const [randomNumber, setrandomNumber] = useState(0);
const onDecrement = useCallback(() => {
//memoised function
setnumber(number - 1);
}, [number]);
const onIncrement = useCallback(() => {
//memoised function
setnumber(number + 1);
}, [number]);
const onRandomNumber = useCallback(() => {
//memoised function
setrandomNumber(Math.random());
}, []);
const expensiveCalculation = num => {
console.log('Calculating...');
for (let i = 0; i < 100000000; i++) {
num += 1;
}
return num;
};
//1. If we dont memoise it this resource extensive function will be called on any state update which will result in lag
//const calculation = expensiveCalculation(number);
//2. If we memoise it, useMemo function will only be called when dependency changes.
const calculation = useMemo(() => expensiveCalculation(number), [number]);
return (
<View style={styles.container}>
<View style={styles.alignCenter}>
<Text onPress={onRandomNumber} style={styles.textStyle}>
Random Number: {randomNumber}
</Text>
<TouchableOpacity onPress={onDecrement}>
<Text style={styles.textStyle}>-</Text>
</TouchableOpacity>
<Text style={styles.textStyle}>Memoised Number: {calculation}</Text>
<TouchableOpacity onPress={onIncrement}>
<Text style={styles.textStyle}>+</Text>
</TouchableOpacity>
</View>
</View>
);
};
export default App;
Remove Console Statements
Posted on October 25, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.