React修改state后组件不重新渲染:天气应用开发问题求助
问题原因&修复方案
1. 阻止表单默认提交行为
你的搜索按钮是type="submit",放在form标签内点击会触发浏览器默认表单提交,导致页面重载回到初始状态,需要在提交事件里调用e.preventDefault()阻止默认行为。
2. 正确获取搜索框输入内容
你没有给搜索框绑定受控状态,点击提交时无法拿到用户输入的地区名,需要在Navbar组件内维护输入框的状态,提交时把值传给父组件的setPlaceName。
3. 修复prop名冲突问题
App.js中给Display组件传递的API密钥用了key作为prop名,key是React的保留属性,不会传递到组件内部的props对象中,需要修改为自定义名称比如apiKey。
4. 让Display组件响应placeName变化
当前Display的useEffect依赖项是空数组,仅会在组件首次挂载时请求接口,当placeName prop更新时不会重新拉取数据,需要把props.placeName添加到useEffect的依赖数组中。
修正后的代码
App.js
import React,{useState} from 'react'; import Navbar from './Navbar.js'; import Display from './Display.js' function App() { const[placeName,setPlaceName] = useState('Raipur') const apiKey = 'not gonna tell'; return ( <> <Navbar setPlaceName={setPlaceName} /> <Display apiKey={apiKey} placeName={placeName} /> </> ); } export default App;
Navbar.js
import React, { useState } from 'react'; function Navbar(props) { const [searchVal, setSearchVal] = useState(''); const handleSubmit = (e) => { e.preventDefault(); if (searchVal.trim()) { props.setPlaceName(searchVal.trim()); } } return( <> <nav className="navbar navbar-expand-lg navbar-dark bg-dark"> <div className="container-fluid"> <a className="navbar-brand" href="/">Navbar</a> <button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="/navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <span className="navbar-toggler-icon"></span> </button> <div className="collapse navbar-collapse" id="navbarSupportedContent"> <ul className="navbar-nav me-auto mb-2 mb-lg-0"> <li className="nav-item"> <a className="nav-link active" aria-current="page" href="/">Home</a> </li> <li className="nav-item"> <a className="nav-link" href="/">Link</a> </li> <li className="nav-item dropdown"> <a className="nav-link dropdown-toggle" href="/" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false"> Dropdown </a> <ul className="dropdown-menu" aria-labelledby="navbarDropdown"> <li><a className="dropdown-item" href="/">Action</a></li> <li><a className="dropdown-item" href="/">Another action</a></li> <li><hr className="dropdown-divider"/></li> <li><a className="dropdown-item" href="/">Something else here</a></li> </ul> </li> <li className="nav-item"> <a className="nav-link disabled">Disabled</a> </li> </ul> <form className="d-flex" onSubmit={handleSubmit}> <input className="form-control me-2" type="search" placeholder="Search" aria-label="Search" value={searchVal} onChange={(e) => setSearchVal(e.target.value)} /> <button className="btn btn-outline-success" type="submit">Search</button> </form> </div> </div> </nav> </> ); } export default Navbar;
Display.js
import React,{useState,useEffect} from 'react'; function Display(props) { const[weatherInfo,setWeatherInfo] = useState({}); const getWeatherInfo = async () =>{ if (!props.placeName) return; try { let url =`https://api.weatherapi.com/v1/current.json?key=${props.apiKey}&q=${props.placeName}&aqi=no`; let res = await fetch(url); let parsedRes = await res.json(); setWeatherInfo(parsedRes.location || {}); } catch (err) { console.error('请求天气数据失败', err); setWeatherInfo({}); } } useEffect(() =>{ getWeatherInfo(); },[props.placeName]) return ( <> <div className="container"> <div className="row"> {Object.values(weatherInfo).map((val, idx)=>{ return( <div className="col" key={idx}> {val} </div> ) })} </div> </div> </> ) } export default Display;
内容的提问来源于stack exchange,提问作者arsh coding
相关产品推荐
相关产品推荐

