如何通过React Context修改父组件状态?搜索组件场景技术问询
问题描述
我要实现一个搜索组件,在子组件Search中获取搜索内容并在父组件Home中使用;目前已经通过React Context在多个子组件(包括Search)中调用相关方法,但不知道怎么通过Search组件修改Home组件的状态。
现有代码
Search组件
import React, { useContext, useState } from "react"; import PhoneBookContext from "../Context/PhoneBookContext"; function Search() { const phoneBookContext = useContext(PhoneBookContext); function handleSearchVal(e) { var searchValue = e.target.value; phoneBookContext.searchVal = searchValue; } return ( <> <ButtonToolbar className="justify-content-between" aria-label="Toolbar with Button groups" > <InputGroup> <InputGroup.Text id="btnGroupAddon2"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className="bi bi-search" viewBox="0 0 16 16" onClick={phoneBookContext.handleSearchClick} > <path d="" /> </svg> </InputGroup.Text> <Form.Control type="search" placeholder="search" aria-label="Input group example" aria-describedby="btnGroupAddon2" onChange={(e) => handleSearchVal(e)} /> </InputGroup> </ButtonToolbar> </> ); } export default Search;
Home组件
import React, { useState, useContext, useEffect } from "react"; import Search from "./Search"; //Import Context import PhoneBookContext from "../Context/PhoneBookContext"; function Home() { var contactList; let filterBySearch; if (JSON.parse(localStorage.getItem("Data")) && Data) { const mergeArrayOfObjects = (original, newdata, selector = "key") => { newdata.forEach((dat) => { const foundIndex = original.findIndex( (ori) => ori[selector] == dat[selector] ); if (foundIndex >= 0) { console.log("something wrong"); } else original.push(dat); }); return original; }; contactList = mergeArrayOfObjects( Data, JSON.parse(localStorage.getItem("Data")), "name" ); } else if (JSON.parse(localStorage.getItem("Data"))) { contactList = JSON.parse(localStorage.getItem("Data")); } else { contactList = Data; } const [contacts, setContacts] = useState(contactList); const [searchVal, setSearchVal] = useState(""); function HandleSearchClick() { setSearchVal(searchVal); if (searchVal === "") { setContacts(contactList); document.location.reload(true); return; } filterBySearch = contactList.filter((Item) => { console.log(searchVal); if (Item.name.toLowerCase().includes(searchVal.toLowerCase())) { return Item; } }); setContacts(filterBySearch); } return ( <div className="container"> <PhoneBookContext.Provider value={{ contactList: contactList, contacts: contacts, handleSearchClick: HandleSearchClick, searchVal: SearchVal, }} > <div className="d-flex justify-content-between"> <div className="title"> <Title /> </div> <div className="search"> <Search /> </div> </div> <ToolBar /> <PaginatedItems itemsPerPage={10} /> </PhoneBookContext.Provider> </div> ); } export default Home;
解决方案
核心问题是你直接修改了Context中的searchVal,这不符合React状态更新的规则,而且Home组件的Context Provider存在笔误。正确做法是把状态更新方法传入Context,让子组件调用方法修改父组件状态。
1. 修改Home组件
- 把
setSearchVal传入Context,让子组件能调用它更新状态 - 修正笔误:
searchVal: SearchVal改为searchVal: searchVal - 用
useEffect监听searchVal变化,自动执行筛选(也可保留点击触发逻辑) - 移除不必要的页面刷新,用状态更新驱动视图变化
修改后的Home组件:
import React, { useState, useEffect } from "react"; import Search from "./Search"; import PhoneBookContext from "../Context/PhoneBookContext"; function Home() { var contactList; if (JSON.parse(localStorage.getItem("Data")) && Data) { const mergeArrayOfObjects = (original, newdata, selector = "key") => { newdata.forEach((dat) => { const foundIndex = original.findIndex( (ori) => ori[selector] == dat[selector] ); if (foundIndex >= 0) { console.log("something wrong"); } else original.push(dat); }); return original; }; contactList = mergeArrayOfObjects( Data, JSON.parse(localStorage.getItem("Data")), "name" ); } else if (JSON.parse(localStorage.getItem("Data"))) { contactList = JSON.parse(localStorage.getItem("Data")); } else { contactList = Data; } const [contacts, setContacts] = useState(contactList); const [searchVal, setSearchVal] = useState(""); // 监听搜索值变化,自动筛选联系人 useEffect(() => { if (!searchVal.trim()) { setContacts(contactList); return; } const filteredList = contactList.filter(item => item.name.toLowerCase().includes(searchVal.toLowerCase()) ); setContacts(filteredList); }, [searchVal, contactList]); // 若保留点击搜索的逻辑,可使用此方法(需传入Context) // function handleSearchClick() { // if (!searchVal.trim()) { // setContacts(contactList); // return; // } // const filteredList = contactList.filter(item => // item.name.toLowerCase().includes(searchVal.toLowerCase()) // ); // setContacts(filteredList); // } return ( <div className="container"> <PhoneBookContext.Provider value={{ contactList, contacts, searchVal, setSearchVal, // handleSearchClick // 若保留点击触发则取消注释 }} > <div className="d-flex justify-content-between"> <div className="title"> <Title /> </div> <div className="search"> <Search /> </div> </div> <ToolBar /> <PaginatedItems itemsPerPage={10} /> </PhoneBookContext.Provider> </div> ); } export default Home;
2. 修改Search组件
- 将输入框改为受控组件,绑定
searchVal和setSearchVal - 移除直接修改Context值的逻辑,改用
setSearchVal更新状态
修改后的Search组件:
import React, { useContext } from "react"; import PhoneBookContext from "../Context/PhoneBookContext"; function Search() { const { searchVal, setSearchVal, handleSearchClick } = useContext(PhoneBookContext); return ( <> <ButtonToolbar className="justify-content-between" aria-label="Toolbar with Button groups" > <InputGroup> <InputGroup.Text id="btnGroupAddon2"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className="bi bi-search" viewBox="0 0 16 16" onClick={handleSearchClick} // 若保留点击触发则保留 > <path d="" /> </svg> </InputGroup.Text> <Form.Control type="search" placeholder="search" aria-label="Input group example" aria-describedby="btnGroupAddon2" value={searchVal} onChange={(e) => setSearchVal(e.target.value)} /> </InputGroup> </ButtonToolbar> </> ); } export default Search;
关键说明
- 受控组件:输入框绑定
value和onChange,确保输入值与父组件状态同步 - Context传递更新方法:通过Context把
setSearchVal传给子组件,子组件调用该方法即可修改父组件状态 - 自动筛选:用
useEffect监听搜索值变化,自动执行筛选逻辑,无需手动刷新页面
内容的提问来源于stack exchange,提问作者Ashraf
相关产品推荐
相关产品推荐

