编辑
有了你的评论,我想我现在明白你想要做什么了,你提供的图片真的很有帮助。如果我没记错的话,只要在 Profile 组件中添加标签,您就想更改学生对象(再次,如果我错了,请纠正我)。这意味着 Profile 组件需要访问处理程序,以便每当添加标签时,它也设置一个新的students 状态。它看起来像这样:
App.js
function App() {
const [students, setStudents] = useState([]);
const [filteredStudents, setFilteredStudents] = useState([]);
const [search, setSearch] = useState("");
const handleTagAdded = (tag, index) => {
setStudents((prevStudents) => {
// We copy object here as the student we're accessing
// is an object, and objects are always stored by reference.
// If we didn't do this, we would be directly mutating
// the student at the index, which is bad practice
const changedStudent = {...prevStudents[index]};
// Check if student has 'tags` and add it if it doesn't.
if (!("tags" in changedStudent)){
changedStudent.tags = [];
}
// Add new tag to array
changedStudent.tags.push(tag);
// Copy array so we can change it
const mutatableStudents = [...prevStudents];
mutatableStudents[index] = changedStudent;
// The state will be set to this array with the student
// at the index we were given changed
return mutatableStudents;
})
}
// Get request and store response in the 'students' state //
useEffect(() => {
axios.get("Link to the API").then((res) => {
const result = res.data.students;
setStudents(result);
});
}, []);
// Filter students by name and tag, then store filtered result in //'filteredStudents'
useEffect(() => {
// Array.filter() is perfect for this situation //
const filteredStudentsByNameAndTag = students.filter((student) => {
const firstName = student.firstName.toLowerCase();
const lastName = student.lastName.toLowerCase();
const fullName = firstName + lastName;
if ("tags" in student){
// You can now do whatever filtering you need to do based on tags
...
}
return fullName.includes(search.toLowerCase()) && yourTagComparison;
});
setFilteredStudents(filteredStudentsByNameAndTag);
}, [search]);
return (
<div>
<SearchBar search={search} onChange={(e) => setSearch(e.target.value)} />
//Second search bar by tag here //
{search.length === 0 &&
// unfiltered students //
}
{search.length !== 0 && (
<div>
{filteredStudents.map((student, index) => (
<Profile
// Some props here //
onTagAdded={handleTagAdded}
// We give the index so Profile adds to the right student
studentIndex={index}
/>
))}
</div>
)}
</div>
);
}
在handleTagAdded 中,我将对象复制到prevStudents[index],因为它是一个引用。如果您不知道我指的是什么(双关语),这听起来可能很奇怪。 Here 是一篇文章的链接,它比我能解释得更好。
Profile.js
function Profile({ onTagAdded, studentIndex }) {
// Other stuff //
const [tags, setTags] = useState([]);
const [tag, setTag] = useState("");
const handleTagKeyPress = (e) => {
if (e.key === "Enter") {
// Use this instead of tags.push, when changing state you always
// must use the `setState()` function. If the new value depends on the
// previous value, you can pass it a function which gets the
// previous value as an argument like below. It is also bad
// practice to change, or 'mutate' the argument you're given
// so we instead copy it and change that.
setTags((previousTags) => [...previousTags].push(tag));
setTag("");
onTagAdded(tag, studentIndex)
}
};
return (
<div>
// Other stuff
<Tag onChange={(e) => setTag(e.target.value)} onKeyPress={handleTagKeyPress} tags={tags} tag={tag} />
</div>
);
}
现在,每个<Profile /> 组件都有自己的标签状态,但是通过使用handleTagAdded(),我们可以根据标签更改每个配置文件组件内的学生。
对于我第一个回答中的困惑,我希望这能解决您的问题!
旧答案
React 中有一个非常重要的概念,称为“Lifting State”。这意味着如果父组件需要访问子组件的状态,一种解决方案是将状态从子组件“提升”到父组件。
您可以在React documentation 中阅读更多相关信息。
在此示例中,您需要将tag 状态从<Profile /> 组件提升到<App /> 组件。这样search 和tag 就在同一个地方,可以比较。
我相信下面的代码符合您的要求:
App.js
function App() {
const [students, setStudents] = useState([]);
const [filteredStudents, setFilteredStudents] = useState([]);
const [tags, setTags] = useState([]);
const [tag, setTag] = useState("");
const [search, setSearch] = useState("");
const handleTagChange = (e) => setTag(e.target.value);
const handleTagKeyPress = (e) => {
if (e.key === "Enter") {
// Use this instead of tags.push, when changing state you always
// must use the `setState()` function. If the new value depends on the
// previous value, you can pass it a function which gets the
// previous value as an argument like below.
setTags((previousTags) => previousTags.push(tag));
setTag("");
}
};
// Get request and store response in the 'students' state //
useEffect(() => {
axios.get("Link to the API").then((res) => {
const result = res.data.students;
setStudents(result);
});
}, []);
// Filter students by name and tag, then store filtered result in //'filteredStudents'
useEffect(() => {
// Array.filter() is perfect for this situation //
const filteredStudentsByNameAndTag = students.filter((student) => {
const firstName = student.firstName.toLowerCase();
const lastName = student.lastName.toLowerCase();
const fullName = firstName + lastName;
return fullName.includes(search.toLowerCase()) && student.tag === tag;
});
setFilteredStudents(filteredStudentsByNameAndTag);
}, [search]);
return (
<div>
<SearchBar search={search} onChange={(e) => setSearch(e.target.value)} />
//Second search bar by tag here //
{search.length == 0 &&
// unfiltered students //
}
{search.length != 0 && (
<div>
{filteredStudents.map((student) => (
<Profile
// Some props here //
onChange={handleTagChange}
onKeyPress={handleTagKeyPress}
tag={tag}
tags={tags}
/>
))}
</div>
)}
</div>
);
}
Profile.js
function Profile({ onChange, onKeyPress, tags, tag }) {
// Other stuff //
return (
<div>
// Other stuff
<Tag onChange={onChange} onKeyPress={onKeyPress} tags={tags} tag={tag} />
</div>
);
}
我们已将tag 状态移至<App /> 组件,因此现在当我们过滤时,我们可以同时使用search 和tag。我还将students.map 更改为students.filter,因为它是过滤数组的更好选择。
我不清楚您想如何过滤标签,所以我假设学生对象将具有tag 属性。请随时纠正我有关数据结构的问题,我会重新格式化它。
希望对您有所帮助,如果您还有其他问题,请告诉我。