React中如何从一个组件的函数更新另一个组件的数组状态
解决思路
你当前渲染电影列表的movies状态存放在父组件App中,搜索逻辑在子组件Topnav内,只需要把App中修改movies状态的方法通过props传递给Topnav,搜索拿到结果后调用该方法更新状态即可自动刷新列表。
第一步:修改App.js
- 给Topnav组件传递修改movies状态的方法
- 把初始请求的API_URL导出给Topnav使用,用于清空搜索词时回到默认热门列表
修改后的App.js代码:
import React, { useEffect, useState } from 'react'; import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; import Movies from './components/Movies'; import Topnav from './components/Topnav'; import './App.css'; import './components/Movies.css'; export const API_KEY = 'api_key=247b6aa143f3f2c0b100c0cbdfb1ac99'; export const BASE_URL = 'https://api.themoviedb.org/3'; export const API_URL = BASE_URL + '/discover/movie?sort_by=popularity.desc&' + API_KEY; export const IMG_URL = 'https://image.tmdb.org/t/p/w500'; export const searchURL = BASE_URL + '/search/movie?'+ API_KEY; function App() { const [movies, setMovies] = useState ([]); useEffect(() => { fetch (API_URL) .then(res => res.json()) .then(data => { console.log(data); setMovies(data.results); }); }, []) return ( <> <Router> {/* 传递setMovies方法给子组件 */} <Topnav updateMovies={setMovies} /> <div className="movie-container"> {movies.length > 0 && movies.map((movie) => ( <Movies key={movie.id} {...movie} />))} </div> <Routes> <Route exact path='/' /> </Routes> </Router> </> ); } export default App;
第二步:修改Topnav.js
- 接收父组件传递的
updateMovies方法 - 搜索拿到结果后调用该方法更新全局电影列表
- 补充清空搜索词时回到默认热门列表的逻辑
修改后的Topnav核心代码:
import React, { useState } from 'react'; import { Link } from 'react-router-dom'; import './Topnav.css'; // 导入初始API_URL import { searchURL, API_URL } from '../App'; // 接收父组件传的updateMovies function Topnav({ updateMovies }) { const [click, setClick] = useState(false) const handleClick = () => setClick(!click) const modeToggle = () => { document.body.classList.toggle('dark') } const [query, setQuery] = useState("") const onChange = (e) => { e.preventDefault(); const searchVal = e.target.value setQuery(searchVal) // 搜索词为空时拉取默认热门列表 if(!searchVal.trim()) { fetch(API_URL) .then(res => res.json()) .then(data => { if(data.results) { updateMovies(data.results) } }) return } // 非空时走搜索接口 fetch(searchURL+`&language=en-US&page=1&include_adult=false&query=${searchVal}`) .then((res) => res.json()) .then((data) => { console.log(data.results); if(data.results) { // 更新父组件的电影列表 updateMovies(data.results) } }) } // 后面的return部分代码不用改,保持原样即可 return ( <> <nav className="topnav"> <div className="topnav-container"> <Link to='/' className='topnav-logo'> <img src={require('../img/logo.png').default} alt="logo" /> </Link> <div className="wrapper"> <form id='search-bar'> <input type="text" placeholder="Search" className="search" id="search" value={query} onChange={onChange} /> </form> <label className="switch"> <input type="checkbox" id="mode-toggle" onChange={modeToggle}/> </label> </div> </div> </nav> </> ) } export default Topnav
可选优化
- 加防抖处理:避免每次输入字符都触发接口请求,可设置300ms延迟再发起请求,减少服务器压力
- 新增加载状态、空结果提示,优化用户体验
- 补充接口请求错误的兜底处理
内容的提问来源于stack exchange,提问作者Dead
相关产品推荐
相关产品推荐

