React中如何阻止输入框onChange触发组件重渲染
解决React组件输入框触发不必要重渲染的问题
问题描述
当前React组件中,输入框每输入一个字符触发onChange事件时,控制台都会打印console.log(result),说明每次输入都会导致整个Home组件重渲染。需要避免这种不必要的重渲染。
解决方案
方案1:拆分搜索表单为独立子组件
将输入相关逻辑抽成单独的子组件,让子组件自行管理输入状态,仅在提交时将城市名传递给父组件。这样输入时只有子组件重渲染,父组件Home不会触发重渲染,也就不会打印多余的result日志。
子组件代码(SearchForm.jsx)
const SearchForm = ({ onSubmit }) => { const [inputCity, setInputCity] = useState(""); const handleSubmit = (e) => { e.preventDefault(); if (inputCity.trim()) { onSubmit(inputCity); } }; return ( <form onSubmit={handleSubmit}> <input type="text" className="home__input" value={inputCity} onChange={(e) => setInputCity(e.target.value)} placeholder="输入城市名" /> <button type="submit" className="weather__button"> Search </button> </form> ); }; export default SearchForm;
修改后的Home组件代码
import "./home.css"; import SearchForm from "./SearchForm"; import { useState, useEffect } from "react"; const Home = () => { const [city, setCity] = useState("kampala"); const [result, setResult] = useState(null); const fetchApi = async (city) => { const data = await fetch( `https://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=你的API密钥` ).then((res) => res.json()) .then((data) => setResult(data)); }; useEffect(() => { fetchApi(city); console.log("heyy"); }, []); const handleSearchSubmit = async (inputCity) => { setCity(inputCity); await fetchApi(inputCity); }; if(result === null) { return <p>Loading</p> } console.log(result); // 现在仅在提交搜索或result变化时打印 return ( <div className="home__container"> <div className="home__img"> <div className="home__hero__info"> <div> <h1>26 C</h1> </div> <div> <h3>Kampala</h3> <p>31 - October - 2022</p> </div> <div> <p>Suuny</p> </div> </div> </div> <div className="home__details"> <div className="weather__div"> <SearchForm onSubmit={handleSearchSubmit} /> <div className="weather__ul"> <ul> <li>Kampala</li> <li>Nairobi</li> <li>Dodoma</li> </ul> </div> <hr /> </div> <div className="weather__div"> <h4 className="h4">Weather Conditions</h4> <div> <span className="weather__details"> <h4>Cloudy</h4> <h3>{result.clouds.all}%</h3> </span> <span className="weather__details"> <h4>Humidity</h4> {/* <h3>{result.main.humidity}%</h3> */} </span> <span className="weather__details"> <h4>Wind</h4> {/* <h3>{wind.speed}</h3> */} </span> <span className="weather__details"> <h4>Rain</h4> <h3>23%</h3> </span> </div> <hr /> </div> </div> </div> ); }; export default Home;
方案2:使用useRef管理输入值(不拆分组件)
如果不想拆分组件,可以用useRef存储输入框的值,输入时不会触发组件状态更新,也就不会导致重渲染。仅在点击搜索按钮时,才更新city状态并触发API请求。
修改后的Home组件代码
import "./home.css"; import { useState, useEffect, useRef } from "react"; const Home = () => { const [city, setCity] = useState("kampala"); const [result, setResult] = useState(null); const inputRef = useRef(null); // 创建ref存储输入框 const fetchApi = async (city) => { const data = await fetch( `https://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=你的API密钥` ).then((res) => res.json()) .then((data) => setResult(data)); }; useEffect(() => { fetchApi(city); console.log("heyy"); }, []); const handleSubmit = async (e) => { e.preventDefault(); const inputCity = inputRef.current.value.trim(); if (inputCity) { setCity(inputCity); await fetchApi(inputCity); } }; if(result === null) { return <p>Loading</p> } console.log(result); // 仅在提交搜索或result变化时打印 return ( <div className="home__container"> <div className="home__img"> <div className="home__hero__info"> <div> <h1>26 C</h1> </div> <div> <h3>Kampala</h3> <p>31 - October - 2022</p> </div> <div> <p>Suuny</p> </div> </div> </div> <div className="home__details"> <div className="weather__div"> <form onSubmit={handleSubmit}> <input type="text" className="home__input" ref={inputRef} // 绑定ref defaultValue={city} // 设置默认值 /> <button type="submit" className="weather__button"> Search </button> </form> <div className="weather__ul"> <ul> <li>Kampala</li> <li>Nairobi</li> <li>Dodoma</li> </ul> </div> <hr /> </div> <div className="weather__div"> <h4 className="h4">Weather Conditions</h4> <div> <span className="weather__details"> <h4>Cloudy</h4> <h3>{result.clouds.all}%</h3> </span> <span className="weather__details"> <h4>Humidity</h4> {/* <h3>{result.main.humidity}%</h3> */} </span> <span className="weather__details"> <h4>Wind</h4> {/* <h3>{wind.speed}</h3> */} </span> <span className="weather__details"> <h4>Rain</h4> <h3>23%</h3> </span> </div> <hr /> </div> </div> </div> ); }; export default Home;
内容的提问来源于stack exchange,提问作者isiagi geofrey
相关产品推荐
相关产品推荐

