React跨组件传递API搜索结果:SearchBar与SearchResultsSection通信问题
解决无共同父组件的React组件间数据传递问题
问题根源
你遇到的onSearchResults is not a function错误,本质是两个组件没有共同父组件,无法通过props传递回调函数。SearchBar组件定义了onSearchResults作为props,但因为没有上层组件能把这个函数传递给它,所以它拿到的onSearchResults是undefined,自然无法调用。
下面是几种可行的解决方法,按推荐程度排序:
解决方案一:React Context API(React内置方案,推荐)
Context API是React官方提供的跨组件共享状态的方案,适合这种无共同父组件的场景。
步骤1:创建搜索上下文
新建一个SearchContext.tsx文件:
import React, { createContext, useContext, useState, ReactNode } from 'react'; // 定义搜索结果类型,和现有代码对齐 interface SearchResult { title: string; overview: string; } // 定义上下文的类型 interface SearchContextType { searchResults: SearchResult[]; setSearchResults: (results: SearchResult[]) => void; } // 创建上下文,默认值用于类型提示 const SearchContext = createContext<SearchContextType | undefined>(undefined); // 自定义Hook,简化上下文的使用 export const useSearchContext = () => { const context = useContext(SearchContext); if (!context) { throw new Error('useSearchContext must be used within a SearchProvider'); } return context; }; // 上下文Provider组件,包裹需要共享状态的组件 export const SearchProvider = ({ children }: { children: ReactNode }) => { const [searchResults, setSearchResults] = useState<SearchResult[]>([]); return ( <SearchContext.Provider value={{ searchResults, setSearchResults }}> {children} </SearchContext.Provider> ); };
步骤2:用Provider包裹应用根组件
在根组件(比如App.tsx)中,用SearchProvider包裹所有需要访问搜索状态的组件:
import { SearchProvider } from './SearchContext'; import SearchBar from './SearchBar'; import SearchResultsSection from './SearchResultsSection'; function App() { return ( <SearchProvider> {/* 这里放你的其他组件,包括SearchBar和SearchResultsSection */} <SearchBar currentPage="homepage" /> <SearchResultsSection /> </SearchProvider> ); } export default App;
步骤3:修改SearchBar组件
去掉onSearchResults props,改用useSearchContext获取setSearchResults:
import React, { useState } from 'react' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faSearch } from '@fortawesome/free-solid-svg-icons' import './SearchBar.css' import { useSearchContext } from './SearchContext'; // 导入自定义Hook interface SearchBarProps { currentPage: string; // 移除onSearchResults } const SearchBar: React.FC<SearchBarProps> = ({ currentPage }) => { const [query, setQuery] = useState<string>('') const { setSearchResults } = useSearchContext(); // 获取更新结果的方法 const handleSearch = async (event: React.FormEvent<HTMLFormElement>) => { event.preventDefault() const apiEndpoint = currentPage === 'homepage' ? `http://localhost:5050/homepage/search/${query}` : currentPage === 'movies' ? `http://localhost:5050/movies/search/${query}` : currentPage === 'series' ? `http://localhost:5050/series/search/${query}` : null if (!apiEndpoint) { console.error('Invalid current page') return } try { const response = await fetch(apiEndpoint, { method: 'GET', headers: { 'Content-Type': 'application/json', }, }) if (response.ok) { const data = await response.json() setSearchResults(data); // 直接调用上下文的方法更新结果 } else { console.error('Error fetching data') } } catch (error) { console.error('Error:', error) } } // 其余JSX代码保持不变 return ( <div> <form onSubmit={handleSearch} className="searchBox"> <div className="input-group input-group-lg"> <input name="search" type="search" className="form-control searchInput" id="search" placeholder={`Search ${ currentPage === 'homepage' ? 'films, series' : currentPage }`} aria-label="Search" value={query} onChange={(e) => setQuery(e.target.value)} /> <button type="submit" className="form-control searchButton" id="submit" > <FontAwesomeIcon icon={faSearch} /> </button> </div> </form> </div> ) } export default SearchBar
步骤4:修改SearchResultsSection组件
去掉results props,改用useSearchContext获取搜索结果:
import React from 'react' import SearchContainer from '../Navbar/SearchContainer' import { useSearchContext } from './SearchContext'; // 导入自定义Hook const SearchResultsSection: React.FC = () => { // 移除props const { searchResults } = useSearchContext(); // 获取搜索结果 return ( <div className="search-results-section"> <h2>Search Results</h2> {searchResults.length > 0 ? ( <ul> {searchResults.map((result, index) => ( <li key={index}> <p>Title: {result.title}</p> <p>Overview: {result.overview}</p> </li> ))} </ul> ) : ( <p>No results found.</p> )} <SearchContainer /> </div> ) } export default SearchResultsSection
解决方案二:状态管理库(如Redux Toolkit)
如果你的应用比较复杂,有很多跨组件状态需要管理,推荐用Redux Toolkit:
- 安装依赖:
npm install @reduxjs/toolkit react-redux - 创建
searchSlice.ts定义状态和更新方法:
import { createSlice, PayloadAction } from '@reduxjs/toolkit'; interface SearchResult { title: string; overview: string; } interface SearchState { results: SearchResult[]; } const initialState: SearchState = { results: [], }; const searchSlice = createSlice({ name: 'search', initialState, reducers: { setSearchResults: (state, action: PayloadAction<SearchResult[]>) => { state.results = action.payload; }, }, }); export const { setSearchResults } = searchSlice.actions; export default searchSlice.reducer;
- 配置Redux store,在根组件用
Provider包裹应用。 - 在SearchBar中用
useDispatch调用setSearchResults,在SearchResultsSection中用useSelector获取结果。
解决方案三:自定义事件(临时方案,不推荐)
如果只是临时测试,可以用浏览器自定义事件,但要注意清理监听避免内存泄漏:
修改SearchBar组件
在搜索完成后派发自定义事件:
// handleSearch函数中,获取data后 window.dispatchEvent(new CustomEvent('searchResults', { detail: data }));
修改SearchResultsSection组件
在组件挂载时监听事件,卸载时移除:
import React, { useState, useEffect } from 'react' import SearchContainer from '../Navbar/SearchContainer' interface SearchResult { title: string; overview: string; } const SearchResultsSection: React.FC = () => { const [results, setResults] = useState<SearchResult[]>([]); useEffect(() => { const handleSearchResults = (e: CustomEvent) => { setResults(e.detail); }; window.addEventListener('searchResults', handleSearchResults as EventListener); // 清理监听 return () => { window.removeEventListener('searchResults', handleSearchResults as EventListener); }; }, []); // 其余JSX代码保持不变 return ( <div className="search-results-section"> <h2>Search Results</h2> {results.length > 0 ? ( <ul> {results.map((result, index) => ( <li key={index}> <p>Title: {result.title}</p> <p>Overview: {result.overview}</p> </li> ))} </ul> ) : ( <p>No results found.</p> )} <SearchContainer /> </div> ) } export default SearchResultsSection
内容的提问来源于stack exchange,提问作者Ravi
相关产品推荐
相关产品推荐

