首页 > 解决方案 > 尽管异步操作中有另一个状态更新,但 React 是否有理由立即重新渲染?

问题描述

值得注意的参考:Can't perform a React state update on an unmounted component

我的目标是更好地理解 React 如何在每次状态更新时或在所有状态更新后重新渲染。

我目前的理解是,如果您有 2 个 state update React re-render on every state update,这将导致。如果您更新一个状态,然后在异步操作中执行另一个状态更新,则会发生此错误。Can't perform a react state update on an unmounted componentone outside of an async operation, and one insidetry, catch, finally

示例Can't perform a react state update on an unmounted component因为 React 不会等待重新渲染,尽管异步操作中有状态更新。当前的修复是不进行状态更新。

功能/主页/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>
    </>
  );
}

组件/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,
  },
});

标签: javascriptreactjsreact-native

解决方案


让我们尝试解释一下重新渲染和警告。我将开始为什么在您的代码中发生重新渲染。

因此,您onBackToCameraonPress处理程序中调用该函数,这就是立即占用重新渲染的原因。为避免这种情况,您应该重写onPress处理程序。

前:

onPress={() => onBackToCamera()}>

后:

onPress={onBackToCamera}>

您会看到此警告Can't perform a react state update on an unmounted component,因为在更新Home组件状态期间,您将替换null为 HTTP 响应。

我将尝试逐步解释如何在控制台中出现此警告。

  1. Camera组件在调用takePicture函数之前按img原样显示null
  2. 然后更新try块内的状态,用 HTTP 响应数据替换空值。
  3. 更新后,将TouchableHighlight显示组件而不是组件,Camera因为该img值不等于 null。
  4. 现在,Camera组件已被卸载,但takePicture函数尚未完成执行,您尝试在finally块中更新已卸载组件的状态。

最后一个动作导致Can't perform a react state update on an unmounted component


推荐阅读