Notable reference: Can't perform a React state update on an unmounted component
My goal is to better understand how React re-render whether on every state update or after all of the state is updated.
My current understanding is React re-render on every state update
which will cause Can't perform a react state update on an unmounted component
if you have 2 state update one outside of an async operation, and one inside
. This error happens if you update a state and then do another state update inside async operation try, catch, finally
.
Example with Can't perform a react state update on an unmounted component
because React does not wait to re-render although there is a state update inside the async operation. The current fix is not to do a state update.
features/Home/index.js
import React from 'react';
import Camera from '../../components/camera';
import {SafeAreaView, TouchableHighlight, Image} from 'react-native';
export default function Home() {
const [img, setImg] = React.useState(null);
function onPicture({uri}) {
setImg(uri);
}
function onBackToCamera() {
setImg(null);
}
return (
<>
<SafeAreaView style={{flex: 1}}>
{img ? (
<TouchableHighlight
style={{flex: 1}}
onPress={() => onBackToCamera()}>
<Image source={{uri: img}} style={{flex: 1}} />
</TouchableHighlight>
) : (
<Camera onPicture={onPicture} />
)}
</SafeAreaView>
</>
);
}
components/camera.js
import React from 'react';
import {RNCamera} from 'react-native-camera';
import Icon from 'react-native-vector-icons/dist/FontAwesome';
import {TouchableOpacity, Alert, StyleSheet} from 'react-native';
export default function Camera({onPicture}) {
let camera;
const [isTakingPicture, setTakePicture] = React.useState(false);
async function takePicture() {
if (camera && !isTakingPicture) {
let options = {
quality: 0.85,
fixOrientation: true,
forceUpOrientation: true,
};
setTakePicture(true);
try {
const data = await camera.takePictureAsync(options);
// Alert.alert('Success', JSON.stringify(data));
onPicture(data);
} catch (err) {
Alert.alert('Error', 'Failed to take picture: ' + (err.message || err));
return;
} finally {
setTakePicture(false);
}
}
}
return (
<RNCamera
ref={(ref) => (camera = ref)}
captureAudio={false}
style={{flex: 1}}
type={RNCamera.Constants.Type.back}
androidCameraPermissionOptions={{
title: 'Permission to use camera',
message: 'We need your permission to use your camera',
buttonPositive: 'Ok',
buttonNegative: 'Cancel',
}}>
<TouchableOpacity
activeOpacity={0.5}
style={styles.btnAlignment}
onPress={takePicture}>
<Icon name="camera" size={50} color="#fff" />
</TouchableOpacity>
</RNCamera>
);
}
const styles = StyleSheet.create({
btnAlignment: {
flex: 1,
flexDirection: 'column',
justifyContent: 'flex-end',
alignItems: 'center',
marginBottom: 20,
},
});