React Native中useState更新时如何避免Camera组件不必要重渲染
问题根因分析
- 核心问题是你把
PlaceHolderImageContainer、CameraContainer两个styled组件定义在了CameraScreen组件内部,每次opacity状态更新触发组件重渲染时,都会生成全新的styled组件实例。React会判定为不同的组件类型,直接卸载旧节点挂载新节点,导致相机反复重启,出现黑屏甚至崩溃。 - 其次相机ref使用了函数式绑定写法,每次重渲染都会执行回调,也会增加不必要的相机实例更新风险。
修复方案
- 将所有styled组件的定义移到
CameraScreen组件外部,动态样式通过props传递,不要直接在styled模板中引用组件内部state - 简化相机ref绑定写法,避免不必要的重挂载
- 可选优化:使用
useCallback包裹事件处理函数,减少不必要的重渲染触发
修正后完整代码
import React, { useRef, useState, useEffect, useCallback } from "react"; import { Camera } from "expo-camera"; import styled from "styled-components"; import { View, TouchableOpacity, Image, Dimensions } from 'react-native'; import { Button } from "react-native-paper"; import Slider from '@react-native-community/slider'; import { Text } from "../../components/typography/text.component"; const CameraBackground = styled(View)` background-color: #222; `; const ImageContainer = styled(View)` flex: 1; background-color: #000; `; const SnapButtonContainer = styled(TouchableOpacity)` position: absolute; z-index: 10000; top: 50%; margin-top: -40px; right: 10px; `; const SnapButton = styled(Button)` border: 1px #b22222 solid; width: 60px; height: 60px; background-color: #8b0000; border-radius: 50px; `; const OpacitySlider = styled(Slider)` z-index: 1000; width: 400px; position: absolute; top: 10px; left: 50%; margin-left: -200px; `; // 移到组件外部,动态opacity通过props获取 const PlaceHolderImageContainer = styled(Image)` width: 100%; height: 100%; position: absolute; left: 0; top: 0; bottom: 0; right: 0; z-index: 999; opacity: ${props => props.opacity}; flex: 1; `; // 移到组件外部,无动态属性 const CameraContainer = styled(Camera)` height: 100%; margin: 0 auto; width: 100% `; export const CameraScreen = ({ route, navigation }) => { const cameraRef = useRef(); const [hasPermission, setHasPermission] = useState(null); const [photo, setPhoto] = useState(null); const [opacity, setOpacity] = useState(0.5); // 用useCallback包裹事件函数,避免重渲染生成新实例 const snap = useCallback(async () => { if (cameraRef.current) { let photoTaken = await cameraRef.current.takePictureAsync(); setPhoto(photoTaken); } }, []); const onOpacityChange = useCallback((v) => { setOpacity(v); }, []); useEffect(() => { (async () => { const { status } = await Camera.requestPermissionsAsync(); setHasPermission(status === 'granted'); })(); }, []); if (hasPermission === null) { return <View />; } if (hasPermission === false) { return <Text>This app does not have access to your camera</Text> } return ( <CameraBackground> <SnapButtonContainer onPress={snap}> <SnapButton /> </SnapButtonContainer> <OpacitySlider minimumValue={0} maximumValue={1} step={0.1} minimumTrackTintColor="#FFFFFF" maximumTrackTintColor="#000000" value={opacity} onValueChange={onOpacityChange} /> {/* 传递opacity属性 */} <PlaceHolderImageContainer opacity={opacity} /> {/* 简化ref绑定 */} <CameraContainer ref={cameraRef} type={Camera.Constants.Type.back} /> </CameraBackground> ); };
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

