React实现温度单位切换:当前天气与预报同步更新方案
温度单位同步切换的React实现方案
问题描述
我正在学习React,现有两个组件:ForecastDay用于展示预报的最高、最低温度,WeatherTempUnit实现温度单位(°C/°F)切换功能。目前点击切换单位时,仅单个组件的温度显示变化,希望实现点击切换后,当前天气和所有预报温度同步切换单位,求可行的实现方案(如状态提升、修改API请求参数等)。
原组件代码
ForecastDay组件
import React from "react"; import WeatherIcon from "./WeatherIcon"; export default function ForecastDay(props) { function showMax() { let max = Math.round(props.data.temperature.maximum); return max; } function showMin() { let min = Math.round(props.data.temperature.minimum); return min; } function showDay() { let date = new Date(props.data.time * 1000); let days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; let day = days[date.getDay()]; return day; } return ( <div className="waether-forcast"> <div className="forecast-date">{showDay()}</div> <div> <WeatherIcon code={props.data.condition.icon} size={43} /> </div> <div> <strong> <span>{showMax()}</span> </strong> °<span> {showMin()}°</span> </div> </div> ); }
WeatherTempUnit组件
export default function WeatherTempUnit(props) { const [unit, setUnit] = useState("celsius"); function showFarn(event) { event.preventDefault(); setUnit("fahrenheit"); } function showCel(event) { event.preventDefault(); setUnit("celsius"); } function fahrenheit() { return Math.round((props.celsius * 9) / 5 + 32); } if (unit === "celsius") { return ( <div> <span>{props.celsius}</span> <span className="units"> °C |{" "} <a href="/" onClick={showFarn}> °F </a> </span> </div> ); } else { return ( <div> <span>{fahrenheit()}</span> <span className="units"> <a href="/" onClick={showCel}> °C{" "} </a> | °F </span> </div> ); } }
可行实现方案
方案一:状态提升(React标准解决方案)
把温度单位的状态从WeatherTempUnit组件提升到当前天气和预报列表的共同父组件,让所有需要根据单位切换显示的组件共享同一个状态。
实现步骤
- 在父组件中定义单位状态和切换方法
- 将单位状态和切换方法通过props传递给
WeatherTempUnit和所有ForecastDay组件 - 修改两个子组件,根据传入的单位状态计算显示对应的温度
修改后的代码示例
父组件(如WeatherApp)
import React, { useState } from "react"; import WeatherTempUnit from "./WeatherTempUnit"; import ForecastDay from "./ForecastDay"; export default function WeatherApp() { // 父组件统一维护单位状态 const [unit, setUnit] = useState("celsius"); // 单位切换方法,传递给子组件调用 const toggleUnit = (newUnit) => { setUnit(newUnit); }; // 示例天气数据(实际从API获取) const currentTemp = 22; const forecastData = [ { time: 1699996800, temperature: { maximum: 25, minimum: 18 }, condition: { icon: "sunny" } }, // 更多预报数据... ]; return ( <div className="weather-app"> {/* 传递单位状态和切换方法给WeatherTempUnit */} <WeatherTempUnit celsius={currentTemp} unit={unit} toggleUnit={toggleUnit} /> {/* 遍历预报数据,传递单位状态给每个ForecastDay */} <div className="forecast-container"> {forecastData.map((dayData, index) => ( <ForecastDay key={index} data={dayData} unit={unit} /> ))} </div> </div> ); }
修改后的WeatherTempUnit组件
import React from "react"; export default function WeatherTempUnit(props) { function showFarn(event) { event.preventDefault(); props.toggleUnit("fahrenheit"); } function showCel(event) { event.preventDefault(); props.toggleUnit("celsius"); } function fahrenheit() { return Math.round((props.celsius * 9) / 5 + 32); } if (props.unit === "celsius") { return ( <div> <span>{props.celsius}</span> <span className="units"> °C |{" "} <a href="/" onClick={showFarn}> °F </a> </span> </div> ); } else { return ( <div> <span>{fahrenheit()}</span> <span className="units"> <a href="/" onClick={showCel}> °C{" "} </a> | °F </span> </div> ); } }
修改后的ForecastDay组件
import React from "react"; import WeatherIcon from "./WeatherIcon"; export default function ForecastDay(props) { // 根据父组件传递的单位计算温度 function showMax() { let max = props.data.temperature.maximum; if (props.unit === "fahrenheit") { max = Math.round((max * 9) / 5 + 32); } else { max = Math.round(max); } return max; } function showMin() { let min = props.data.temperature.minimum; if (props.unit === "fahrenheit") { min = Math.round((min * 9) / 5 + 32); } else { min = Math.round(min); } return min; } function showDay() { let date = new Date(props.data.time * 1000); let days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; let day = days[date.getDay()]; return day; } return ( <div className="waether-forcast"> <div className="forecast-date">{showDay()}</div> <div> <WeatherIcon code={props.data.condition.icon} size={43} /> </div> <div> <strong> <span>{showMax()}</span>° </strong> <span> {showMin()}°</span> </div> </div> ); }
方案二:修改API请求参数(后端切换单位)
如果你的天气API支持直接返回不同单位的温度数据,可以在切换单位时重新请求API,获取对应单位的温度后更新所有组件的数据源。
实现步骤
- 在父组件中维护单位状态和天气数据状态
- 切换单位时,携带对应单位参数重新请求API
- 将新的天气数据传递给所有子组件直接显示
关键代码示例(父组件)
import React, { useState, useEffect } from "react"; import WeatherTempUnit from "./WeatherTempUnit"; import ForecastDay from "./ForecastDay"; export default function WeatherApp() { const [unit, setUnit] = useState("celsius"); const [weatherData, setWeatherData] = useState(null); // 单位变化时重新请求API useEffect(() => { // 假设API支持units参数,值为celsius/fahrenheit fetch(`https://your-weather-api.com/data?units=${unit}`) .then(res => res.json()) .then(data => { setWeatherData(data); }); }, [unit]); const toggleUnit = (newUnit) => { setUnit(newUnit); }; if (!weatherData) return <div>加载中...</div>; return ( <div className="weather-app"> <WeatherTempUnit celsius={weatherData.current.temp} unit={unit} toggleUnit={toggleUnit} /> <div className="forecast-container"> {weatherData.forecast.map((dayData, index) => ( <ForecastDay key={index} data={dayData} /> ))} </div> </div> ); }
这种方式下,ForecastDay组件无需修改温度计算逻辑,直接显示API返回的对应单位温度即可。
方案对比
- 状态提升:无需额外API请求,前端完成温度转换,适合API不支持单位切换或希望减少请求的场景,是React状态共享的标准做法。
- 修改API参数:前端逻辑更简洁,温度转换由后端处理,数据准确性更高,适合API支持单位参数的场景。
内容的提问来源于stack exchange,提问作者Fox Black
相关产品推荐
相关产品推荐

