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

React跨组件共享天气API数据 无需重复请求渲染天气图标

跨组件复用天气数据实现方案

React函数组件内部通过useState声明的状态属于组件私有闭包变量,无法直接导出给其他组件使用。要实现「仅发起一次API请求、全项目任意组件复用天气数据、单独渲染天气图标」的需求,最通用的无额外依赖方案是使用React Context做状态提升,把数据请求和公共逻辑抽离到全局共享层。


具体实现步骤

1. 抽离全局天气Context

新建天气上下文文件,把API请求、天气数据、图标匹配逻辑全部统一管理,整个应用只会初始化一次、发一次请求:

// src/context/WeatherContext.tsx
import { createContext, useContext, useEffect, useState, ReactNode } from 'react';
import axios from 'axios';
import { Weather } from '../types';
import { MdWbSunny } from 'react-icons/md';
import { IoIosPartlySunny } from 'react-icons/io';
import { BsFillCloudSnowFill } from 'react-icons/bs';

// 定义上下文返回值类型
type WeatherContextType = {
  weather: Weather | null;
  loading: boolean;
  getWeatherIcon: (forecast?: string) => ReactNode;
};

const WeatherContext = createContext<WeatherContextType | undefined>(undefined);

// 全局Provider组件,包裹在应用最外层
export const WeatherProvider = ({ children }: { children: ReactNode }) => {
  const [weather, setWeather] = useState<Weather | null>(null);
  const [loading, setLoading] = useState(true);

  const fetchWeatherData = async () => {
    setLoading(true);
    const res = await axios.get('http://mock-api-call/weather/get-weather');
    setWeather(res.data.result.weather);
    setLoading(false);
  };

  // 仅在Provider初始化时请求一次数据
  useEffect(() => {
    fetchWeatherData();
  }, []);

  // 统一抽离图标匹配逻辑,所有组件复用,无需重复写判断
  const getWeatherIcon = (forecast?: string) => {
    switch (forecast) {
      case 'Sunny':
        return <MdWbSunny data-testid="sunny" />;
      case 'Snowing':
        return <BsFillCloudSnowFill data-testid="snowing" />;
      default:
        return <IoIosPartlySunny data-testid="overcast" />;
    }
  };

  return (
    <WeatherContext.Provider value={{ weather, loading, getWeatherIcon }}>
      {children}
    </WeatherContext.Provider>
  );
};

// 封装自定义调用Hook,简化组件使用逻辑
export const useWeather = () => {
  const context = useContext(WeatherContext);
  if (!context) throw new Error('useWeather 必须在 WeatherProvider 内部使用');
  return context;
};

2. 在应用根层挂载Provider

把WeatherProvider包裹在项目根组件的最外层,保证全局只有一份天气数据实例:

// src/main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { WeatherProvider } from './context/WeatherContext';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <WeatherProvider>
    <App />
  </WeatherProvider>
);

3. 改造原有天气组件

移除原组件内部的状态声明和API请求逻辑,直接通过useWeather消费全局数据:

import { Title, Text, Container } from '@mantine/core';
import { useWeather } from '../context/WeatherContext';

const WeatherComponent = () => {
  const { weather, loading, getWeatherIcon } = useWeather();

  if (loading) return <Container>天气数据加载中...</Container>;

  return (
    <Container>
      <Title order={2}>
        {getWeatherIcon(weather?.forcast)}
      </Title>
      <Text size="xl" data-testid="forcast">
        {weather?.forcast}
      </Text>
      <Text size="lg" data-testid="temp">
        Temp: {`${weather?.min} to ${weather?.max}`}
      </Text>
      <Text size="md" data-testid="description">
        {weather?.description}
      </Text>
    </Container>
  );
};

export default WeatherComponent;

4. 任意组件单独渲染天气图标

其他任何需要用到天气数据或者天气图标的组件,直接调用useWeather即可,完全不会重复发起API请求:

// 示例:页面头部组件仅展示天气图标
import { useWeather } from '../context/WeatherContext';

const SiteHeader = () => {
  const { weather, loading, getWeatherIcon } = useWeather();
  return (
    <header className="site-header">
      <h1>我的站点</h1>
      {!loading && <div className="header-weather">{getWeatherIcon(weather?.forcast)}</div>}
    </header>
  );
};

export default SiteHeader;

其他可选方案

如果项目已经接入了Zustand、Redux、Jotai这类全局状态管理库,直接把天气数据、请求逻辑、图标匹配方法存在全局Store里即可,实现逻辑和Context方案一致,同样可以保证单次请求、全局共享数据。

注意:不要尝试通过props层层传递天气数据,嵌套层级深的时候维护成本极高,也容易触发不必要的重渲染。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 13:31:11