You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 21:20:24