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

React Native应用调用API后组件首次不更新,需在VS Code保存两次才正常显示

问题根源

  • Context层拿到API返回的用户数据后,仅写入AsyncStorage,未调用setUser更新全局状态,不会触发任何依赖该状态的组件重渲染。
  • WelcomeChamado页面的初始化useEffect只在组件挂载时执行一次,此时API请求大概率还未完成,AsyncStorage里的用户数据还是旧值/空值,后续API请求完成写入新值后,页面没有监听Storage变化,不会主动重新读取数据。
  • 你在VS Code保存文件触发热更新时,组件重新挂载,此时AsyncStorage已经有了上次API写入的最新数据,所以才会正常显示。

修复方案

1. 修改user.js(Context文件)

拿到API返回数据后先更新全局状态,再写入持久化存储,同时补全Context未定义的状态变量:

import React, { createContext, useEffect, useState, useContext } from "react"
import AsyncStorage from "@react-native-async-storage/async-storage"

import ApiRequest from '../services/Api';

const UserContext = createContext({})

export const UserProvider = ({ children }) => {
  const [user, setUser] = useState({})
  const [services, setServices] = useState([])
  // 补全你Context value里用到的未声明变量,根据业务逻辑调整初始值
  const [currentService, setCurrentService] = useState(null)
  const [serviceDate, setServiceDate] = useState('')
  const [serviceType, setServiceType] = useState('')

  useEffect(() => {
    let isSubscribed = true;

    const getStorage = async (isSubscribed) => {
      const storagedToken = await AsyncStorage.getItem('token');
      const storagedId = await AsyncStorage.getItem('id');

      if(storagedToken && storagedId) {
        isSubscribed ? getUser(storagedToken, storagedId) : null;
      }
    }

    getStorage(isSubscribed);

    return () => (isSubscribed = false);
  }, [])

  async function getUser(storagedToken, storagedId) {
    try {
      const resp = await ApiRequest.getUser(storagedToken, storagedId)
      const userData = resp.Cadastro
      // 新增:更新全局状态,触发组件重渲染
      setUser(userData)
      await AsyncStorage.setItem('user', JSON.stringify(userData));
    } catch (error) {
      console.log('Não foi possível recuperar os dados do usuário, tente novamente.')
    }
  }

  return (
    <UserContext.Provider value={{ user, currentService, services, setServiceDate, setServiceType, setCurrentService }}>
      {children}
    </UserContext.Provider>
  )
}

export const useUser = () => {
  return useContext(UserContext)
}

2. 修改WelcomeChamado.js

无需自己维护用户状态,直接从Context取数据,避免和全局数据不同步:

import React, { useEffect, useState } from 'react'
import { View, Image, ScrollView, StyleSheet, TouchableOpacity } from 'react-native'
import { LinearGradient } from 'expo-linear-gradient';
import { initialWindowMetrics, SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context';
import NavBar from '../../../components/NavBar';

import { TextMuseo300, TextMuseo500 } from '../../../components/fonts/TextFonts';

import { theme } from '../../../global/theme';
import { TextInputMuseo300 } from '../../../components/fonts/TextInputFonts';
import AsyncStorage from '@react-native-async-storage/async-storage';

const { principalOne, principalTwo, principalThree, destaque, branco } = theme.colors;

import { Entypo } from '@expo/vector-icons';
// 引入全局用户钩子
import { useUser } from '你的user.js对应路径'

export function WelcomeChamado({ navigation }) {
  const insets = useSafeAreaInsets();
  // 直接从全局Context取用户数据,无需自己维护状态
  const { user } = useUser()
  const [foto, setFoto] = useState('');

  useEffect(() => {
    const getInfo = async () => {
      const storagedFoto = await AsyncStorage.getItem('@lica:foto');
      setFoto(storagedFoto);
    }
    getInfo();
  }, []);


  const nextScreen = () => {
    navigation.navigate("AguardarChamado");
  }

  // 原有渲染逻辑不变,直接使用user变量即可
  return (
    <ScrollView 
      showsVerticalScrollIndicator={false}
      contentContainerStyle={{
        flexGrow: 1,
    }}>
      <SafeAreaProvider initialMetrics={initialWindowMetrics} 
        style={
          styles.container, 
          {
            paddingTop: insets.top,
            paddingLeft: insets.left,
            paddingBottom: insets.bottom,
            paddingRight: insets.right,
          }
      }>
        <LinearGradient 
          start={{ x: 0, y: 0.5 }}
          end={{ x: 1, y: 0.5 }}
          locations={[0, 0.2, 0.4, 0.6, 0.8, 1]}
          colors={['#8EF4F0', '#D6FDFA', '#F8FEFE', '#F8FEFE', '#D6FDFA', '#8EF4F0']} 
          style={{
            flex: 1
        }}>
          <NavBar navigation={navigation} />
          <View style={{
            alignItems: 'center',
            marginTop: '5%'
          }}>
            <TextMuseo300 style={{
              color: '#2E8F71',
              marginTop: '1%',
              fontWeight: 'bold',
              marginBottom: 10
            }}>Olá</TextMuseo300>
            <View style={{
              justifyContent: 'center',
              alignItems: 'center',
            }}>
              <Image 
                source={{ uri: `data:image/gif;base64,${foto}` }}
                style={{ 
                  width: 100, 
                  height: 100,
                  borderRadius: 50,
                  borderWidth: 1,
                  borderColor: destaque,
                }}
                resizeMode="cover"
              />
              <View style={{
                position: 'absolute',
                top: -5,
                right: 0,
                backgroundColor: '#2E8F71',
                width: 32,
                height: 32,
                borderRadius: 16,
                paddingLeft: 2,
                alignItems: 'center',
                justifyContent: 'center'
              }}>
                <Entypo 
                  name="bell"
                  size={25}
                  color="#FFF"
                />
              </View>
            </View>
            <TextMuseo300 style={{
              color: '#2E8F71',
              marginTop: '1%',
              fontWeight: 'bold',
              marginTop: 10
            }}>{user?.CadastroNome}</TextMuseo300>
            <TextMuseo300 style={{
              color: '#2E8F71',
              marginTop: '1%'
            }}>{user?.CadastroUser}</TextMuseo300>
          </View>

          <View style={{
            marginTop: 30,
            alignItems: 'center',
          }}>
            <TextMuseo300 style={{
              color: '#2E8F71',
              marginTop: '2%',
              maxWidth: 120,
              fontSize: 20,
              textAlign: 'center'
            }}>Você está disponível para receber chamados?</TextMuseo300>

            <TouchableOpacity onPress={nextScreen}>
              <View style={{
                justifyContent: 'center',
                alignItems: 'center',
                backgroundColor: '#2E8F71',
                borderRadius: 10,
                width: 120,
                height: 30,
                marginTop: 50
              }}>
                <TextMuseo300 style={{
                  color: '#FFF',
                  marginTop: '2%',
                  maxWidth: 120,
                  fontSize: 20,
                  fontWeight: 'bold',
                  textAlign: 'center'
                }}>Sim, estou.</TextMuseo300>
              </View>
            </TouchableOpacity>
          </View>
        </LinearGradient>
      </SafeAreaProvider>
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  center: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
});

优化建议

  • 优先用Context全局状态做渲染数据源,AsyncStorage仅作为持久化缓存使用,不要让业务组件直接读取Storage作为渲染源,避免数据不同步问题。
  • 可以在APP启动时新增启动加载页,等用户核心数据加载完成后再渲染业务页面,避免出现字段空白的情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 23:36:03