【问题标题】:React: Passing up data from child component into parent does not update values in parent反应:将数据从子组件传递到父组件不会更新父组件中的值
【发布时间】:2020-11-21 13:07:59
【问题描述】:

// 编辑 --

这可能会有所帮助: Project Hatchways

问题链接 - Issue

就目前的代码而言,标签的结果仍未呈现结果。

我有一个组件 App.js 可以渲染一些子元素。其中之一是 2 个搜索栏。第二个搜索栏TagSearch 应该呈现标签创建的结果。我想要做的是从标签所在的Student 传递数据,并将它们传递给 App 组件,以便将它们注入我的 Fuse 实例以便搜索它们。我试图在 App.js 中创建一个函数update,然后将其传递给 Student.js,以便在用户搜索标签时在父级中更新标签。出于某种原因,我收到一个 TypeError,指出 update 不是函数。

我放入控制台日志以跟踪标签出现的位置。这些标签在 Student.js 中看起来非常好,但是当我在 App.js 中控制台记录它们时,这些标签只是显示为一个空数组,这告诉我它们没有正确地从 Student.js 向上传递组件树到 App。 js.

// App.js

import axios from "axios";
import Fuse from "fuse.js";
import Student from "./components/Student";
import Search from "./components/Search";
import TagSearch from "./components/TagSearch";
import "./App.css";

function App() {
  const [loading, setLoading] = useState(true);
  const [students, setStudents] = useState([]);
  const [query, updateQuery] = useState("");
  const [tags, setTags] = useState([]);
  const [tagQuery, setTagQuery] = useState("");
  console.log("tags from app: ", tags);

  const getStudents = async () => {
    setLoading(true);
    try {
      const url = `private url for assignment`;
      const response = await axios.get(url);
      setStudents(response.data.students);
      setLoading(false);
    } catch (err) {
      console.log("Error: ", err);
    }
  };

  const fuse = new Fuse(students, {
    keys: ["firstName", "lastName"],
    includeMatches: true,
    minMatchCharLength: 2,
  });

  const tagFuse = new Fuse(tags, {
    keys: ["text", "id"],
    includesMatches: true,
    minMatchCharLength: 2,
  });

  function handleChange(e) {
    updateQuery(e.target.value);
  }

  function handleTags(e) {
    setTagQuery(e.target.value);
  }

  const results = fuse.search(query);
  const studentResults = query ? results.map((s) => s.item) : students;

  const tagResults = tagFuse.search(tagQuery);
  const taggedResults = tagQuery ? tagResults.map((s) => s.item) : tags;

  const update = (t) => {
    t = tags; // changed this to make sure t is tags from this component's state
    setTags(t);
  };

  useEffect(() => {
    getStudents();
  }, []);

  if (loading) return "Loading ...";
  return (
    <div className="App">
      <main>
        <Search query={query} handleChange={handleChange} />
        <TagSearch query={tagQuery} handleTags={handleTags} />
        {studentResults &&
          studentResults.map((s, key) => <Student key={key} students={s} update={update} />)}
        {taggedResults &&
          taggedResults.map((s, key) => (
            <Student key={key} students={s} update={update} />
          ))}
      </main>
    </div>
  );
}

export default App;

// Student.js

import Collapsible from "../components/Collapsible";
import findAverage from "../helpers/findAverage";
import Styles from "../styles/StudentStyles";

const KeyCodes = {
  comma: 188,
  enter: 13,
};

const delimiters = [KeyCodes.comma, KeyCodes.enter];

const Student = ({ students, update }) => {
  const [isOpened, setIsOpened] = useState(false);
  const [tags, setTags] = useState([]);

  const collapse = () => {
    setIsOpened(!isOpened);
  };

  const handleDelete = (i) => {
    const deleted = tags.filter((tag, index) => index !== i);
    setTags(deleted);
  };

  const handleAddition = (tag, i) => {
    setTags([...tags, tag]);
  };

  useEffect(() => {
    update(tags);
  }, []);

  return (
    <Styles>
      <div className="student-container">
        <img src={students.pic} alt={students.firstName} />
        <div className="student-details">
          <h1>
            {students.firstName} {students.lastName}
          </h1>
          <p>Email: {students.email}</p>
          <p>Company: {students.company}</p>
          <p>Skill: {students.skill}</p>
          <p>Average: {findAverage(students.grades)}</p>
          <Collapsible
            students={students}
            delimiters={delimiters}
            handleDelete={handleDelete}
            handleAddition={handleAddition}
            isOpened={isOpened}
            tags={tags}
          />
        </div>
      </div>
      <button onClick={collapse}>+</button>
    </Styles>
  );
};

export default Student;

【问题讨论】:

  • 我立即注意到的是,当有搜索结果时创建的Student 组件没有传递更新函数。然而,useEffect 在第一次渲染时仍会调用update。
  • @CoryHarper 所以我很困惑。我是否应该用更新函数将搜索结果包装在App.js 中,然后传递更新函数,并留下useEffect?
  • 对您的Student 组件进行健全性检查,console.log(update)
  • 我认为@Cory Harper 的意思是简单地将update 作为道具传递。
  • 您可以简单地添加一个回调函数,当您将数据从student 发送到app 时,该回调函数将被触发,然后在该回调中更改父级的状态跨度>

标签: reactjs state parent-child


【解决方案1】:

Ciao,每次在Student 中更新tags 时,请尝试调用update 函数。像这样的:

const handleDelete = (i) => {
    const deleted = tags.filter((tag, index) => index !== i);
    setTags(deleted);
    update(deleted);
  };

  const handleAddition = (tag, i) => {
    let result = tags;
    result.push(tag);
    setTags(result);
    update(result);
  };

这样,每次在Student中更改tags,都会更新App状态。

另一种方法是使用useEffect deps 列表。在Student 中,修改useEffect 如下:

useEffect(() => {
    update(tags);
  }, [tags]);

这意味着,每次tags 更新时,都会触发useEffect 并调用update 函数。

【讨论】:

  • 当我添加这个时,我收到了这个错误Uncaught TypeError: Cannot read property 'map' of undefined。我会说 useEffect 仍然是 useEffect(() => update(tags), [])
  • 所以你采纳了我的第一个建议。准确地说,哪个地图失败了?我问你是因为,正如我从你的代码中看到的那样,你没有在标签数组中调用 .map 函数。我可以问你一件事吗?如果你在Appupdate函数中放一个console.log,你会收到来自Student的一些值吗?
  • 因此,当我将 tags 放入 useEffect 括号中时,它会因 .map 错误而中断,但当我将其留空时,我不会收到任何错误,但结果也不会更新.
猜你喜欢
  • 2023-04-03
  • 1970-01-01
  • 1970-01-01
  • 2019-02-27
  • 2017-04-20
  • 2020-03-04
  • 2019-12-04
相关资源
最近更新 更多