You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native中useEffect本地未随状态变化触发,Snack中正常

问题:本地运行时红球需点击3次才移动,Expo Snack表现正常

问题场景

在Expo环境中点击按钮,红球每次都会按预期移动;但本地运行同一代码(仅ChoiceButton导入路径不同)时,红球每点击3次才会移动一次,屏幕录制显示useEffect调用时机异常。

核心原因

当前代码通过RangeMode状态中转触发位置更新,但点击同一按钮时,RangeMode被重复设置为相同值(比如多次点击"Soldat i värn"按钮时,RangeMode一直设为0)。React会跳过相同状态值的更新,导致依赖RangeMode的useEffect无法执行,进而不触发红球位置计算。

同时,RangeMode属于冗余中转状态,直接依赖按钮状态和选中枪支状态即可触发更新。

修复方案

  1. 移除冗余的RangeMode状态
  2. 让位置更新逻辑直接依赖buttonHiddenSoldier和choosenGun状态
  3. 简化按钮点击函数,无需设置RangeMode

修改后的关键代码

// 移除RangeMode相关状态及对应useEffect
// const [RangeMode, setRangeMode] = useState(0);

// 替换为直接依赖按钮和枪支状态的useEffect
useEffect(() => {
  console.log("更新红球位置");
  update_shooting_range();
}, [buttonHiddenSoldier, choosenGun]);

// 简化按钮点击逻辑
const updateButtonHiddenSoldier = () => {
  setbuttonHiddenSoldier(true);
  setbuttonSoldierInOpen(false);
  // 移除setRangeMode(0);
};

const updateButtonSoliderInOpen = () => {
  setbuttonHiddenSoldier(false);
  setbuttonSoldierInOpen(true);
  // 移除setRangeMode(1);
};

完整优化后代码

import React, { useEffect, useState, useRef } from "react";
import { View, Text, SafeAreaView, ImageBackground } from "react-native";

import ChoiceButton from "../../components/ChoiceButton";

const ShootingDistance = ({ navigation }) => {
  const [targetLayout, setTargetLayout] = useState(undefined);
  const [parentContainer, setParentContainer] = useState(undefined);
  const [ballPlacement, setBallPlacement] = useState(450);
  const [choosenGun, setchoosenGun] = useState("Kpist");
  const [buttonHiddenSoldier, setbuttonHiddenSoldier] = useState(true);
  const [buttonSoldierInOpen, setbuttonSoldierInOpen] = useState(false);
  const [gunImage, setGunImage] = useState(null);

  // 直接依赖按钮状态和选中枪支触发位置更新
  useEffect(() => {
    console.log("更新红球位置");
    update_shooting_range();
  }, [buttonHiddenSoldier, choosenGun]);

  const setValueGun = (value) => {
    if (value == 0) {
      setchoosenGun("Ak4");
    } else if (value == 1) {
      setchoosenGun("Ak4 Kikarsikte");
    } else if (value == 2) {
      setchoosenGun("Kpist");
    } else if (value == 3) {
      setchoosenGun("Kulspruta 58");
    }
  };

  const update_shooting_range = () => {
    if (choosenGun == "Ak4") {
      setBallPlacement(buttonHiddenSoldier ? 300 : 500);
    } else if (choosenGun == "Ak4 Kikarsikte") {
      setBallPlacement(buttonHiddenSoldier ? 400 : 600);
    } else if (choosenGun == "Kpist") {
      setBallPlacement(buttonHiddenSoldier ? 150 : 250);
    } else if (choosenGun == "Kulspruta 58") {
      setBallPlacement(buttonHiddenSoldier ? 400 : 600);
    }
  };

  const updateButtonHiddenSoldier = () => {
    setbuttonHiddenSoldier(true);
    setbuttonSoldierInOpen(false);
  };

  const updateButtonSoliderInOpen = () => {
    setbuttonHiddenSoldier(false);
    setbuttonSoldierInOpen(true);
  };

  const activateButtonHiddenSoldier = () => {
    updateButtonHiddenSoldier();
  };

  const activateButtonSoliderInOpen = () => {
    updateButtonSoliderInOpen();
  };

  // 合并视图引用为对象,简化代码结构
  const viewRefs = {
    600: useRef(null),
    550: useRef(null),
    500: useRef(null),
    450: useRef(null),
    400: useRef(null),
    350: useRef(null),
    300: useRef(null),
    250: useRef(null),
    200: useRef(null),
    150: useRef(null),
    100: useRef(null),
    50: useRef(null),
  };

  useEffect(() => {
    const ref = viewRefs[ballPlacement]?.current;
    if (ref) {
      ref.measure((x, y, width, height) => {
        setTargetLayout({ x, y, width, height });
      });
    }
  }, [ballPlacement]);

  return (
    <ImageBackground
      source={require("../images/backgrounds/background.png")}
      resizeMode="cover"
      style={{ flex: 1, width: "100%", height: "100%" }}
    >
      <SafeAreaView style={{ flex: 1, margin: 10 }}>
        {targetLayout && parentContainer && (
          <View
            style={{
              backgroundColor: "red",
              width: targetLayout.height / 2,
              height: targetLayout.height / 2,
              borderRadius: targetLayout.height / 2,
              position: "absolute",
              zIndex: 2,
              left:
                targetLayout.x +
                parentContainer.x +
                targetLayout.width / 2 -
                targetLayout.height / 2,
              top: targetLayout.y + parentContainer.y + targetLayout.height / 4,
            }}
          />
        )}
        <View style={{ flex: 1 }}>
          <Text
            style={{
              flex: 1,
              color: "white",
              fontSize: 30,
              textAlign: "center",
              marginTop: 20,
            }}
          >
            Skjutavstånd
          </Text>
          <Text
            style={{
              flex: 1,
              color: "white",
              fontSize: 20,
              textAlign: "center",
            }}
          >
            Välj ett vapen samt typ av mål för att se max rekommenderat
            skjutavstånd.
          </Text>
        </View>

        <View
          style={{
            flex: 2,
            backgroundColor: "lightgray",
            width: "90%",
            marginHorizontal: 20,
            marginTop: 20,
          }}
          onLayout={(event) => {
            const layout = event.nativeEvent.layout;
            setParentContainer(layout);
          }}
        >
          <View
            style={{
              flex: 1,
              borderColor: "black",
              borderStyle: "solid",
              backgroundColor: "lightgray",
              borderBottomWidth: 1,
            }}
            ref={viewRefs[600]}
          >
            <Text style={{ textAlign: "right" }}>600 meter</Text>
          </View>
          <View
            style={{
              flex: 1,
              borderColor: "black",
              borderStyle: "solid",
              borderBottomWidth: 3,
            }}
            ref={viewRefs[550]}
          >
            <Text style={{ textAlign: "right" }}>550 meter</Text>
          </View>
          <View
            style={{
              flex: 1,
              borderColor: "black",
              borderStyle: "solid",
              borderBottomWidth: 1,
            }}
            ref={viewRefs[500]}
          >
            <Text style={{ textAlign: "right" }}>500 meter</Text>
          </View>
          <View
            style={{
              flex: 1,
              borderColor: "black",
              borderStyle: "solid",
              borderBottomWidth: 3,
            }}
            ref={viewRefs[450]}
          >
            <Text style={{ textAlign: "right" }}>450 meter</Text>
          </View>
          <View
            style={{
              flex: 1,
              borderColor: "black",
              borderStyle: "solid",
              borderBottomWidth: 1,
            }}
            ref={viewRefs[400]}
          >
            <Text style={{ textAlign: "right" }}>400 meter</Text>
          </View>
          <View
            style={{
              flex: 1,
              borderColor: "black",
              borderStyle: "solid",
              borderBottomWidth: 3,
            }}
            ref={viewRefs[350]}
          >
            <Text style={{ textAlign: "right" }}>350 meter</Text>
          </View>
          <View
            style={{
              flex: 1,
              borderColor: "black",
              borderStyle: "solid",
              borderBottomWidth: 1,
            }}
            ref={viewRefs[300]}
          >
            <Text style={{ textAlign: "right" }}>300 meter</Text>
          </View>
          <View
            style={{
              flex: 1,
              borderColor: "black",
              borderStyle: "solid",
              borderBottomWidth: 3,
            }}
            ref={viewRefs[250]}
          >
            <Text style={{ textAlign: "right" }}>250 meter</Text>
          </View>
          <View
            style={{
              flex: 1,
              borderColor: "black",
              borderStyle: "solid",
              borderBottomWidth: 1,
            }}
            ref={viewRefs[200]}
          >
            <Text style={{ textAlign: "right" }}>200 meter</Text>
          </View>
          <View
            style={{
              flex: 1,
              borderColor: "black",
              borderStyle: "solid",
              borderBottomWidth: 3,
            }}
            ref={viewRefs[150]}
          >
            <Text style={{ textAlign: "right" }}>150 meter</Text>
          </View>
          <View
            style={{
              flex: 1,
              borderColor: "black",
              borderStyle: "solid",
              borderBottomWidth: 1,
            }}
            ref={viewRefs[100]}
          >
            <Text style={{ textAlign: "right" }}>100 meter</Text>
          </View>
          <View
            style={{
              flex: 1,
              borderColor: "black",
              borderStyle: "solid",
              borderBottomWidth: 3,
              flexDirection: "row",
              justifyContent: "space-between",
            }}
            ref={viewRefs[50]}
          >
            <Text style={{ flex: 1, textAlign: "right" }}>50 meter</Text>
          </View>
        </View>

        <View
          style={{
            flex: 1,
            justifyContent: "center",
            alignItems: "center",
          }}
        >
          <Text style={{ flex: 1, color: "white", fontSize: 22 }}>
            Välj vapen (4)
          </Text>
          <Text
            style={{
              flex: 1,
              color: "white",
              fontSize: 14,
              color: "yellow",
              fontWeight: "bold",
            }}
          >
            {choosenGun}
          </Text>
        </View>
        <View
          style={{
            flex: 1,
            justifyContent: "center",
            alignItems: "center",
          }}
        >
          <Text style={{ flex: 1, color: "white", fontSize: 28 }}>
            Typ av mål
          </Text>
          <View
            style={{
              flex: 1,
              flexDirection: "row",
              justifyContent: "space-between",
              marginBottom: 20,
            }}
          >
            <ChoiceButton
              ClickFunction={activateButtonHiddenSoldier}
              ButtonState={buttonHiddenSoldier}
              ButtonText="Soldat i värn"
              MarginRight={10}
            />

            <ChoiceButton
              ClickFunction={activateButtonSoliderInOpen}
              ButtonState={buttonSoldierInOpen}
              ButtonText="Soldat öppet"
            />
          </View>
        </View>
      </SafeAreaView>
    </ImageBackground>
  );
};

export default ShootingDistance;

额外优化说明

  • 将分散的视图引用合并为对象,简化代码结构
  • 用三元表达式简化update_shooting_range中的条件判断
  • 移除measure中未使用的冗余参数pageX和pageY

内容的提问来源于stack exchange,提问作者Jhnsbrst

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 15:05:20