For example: I'm displaying multiple Student
objects to the website that's coming from an API. Each Student
object contains an array of their test Grades
along with other student information. Now, I want to display each Student
's information onto the screen but instead of displaying the student's Grades
, I want each Student
to have a "+"
Button
which will be to toggle the display of the student's Grades
.
I'm using the useState hook from React. My problem is that when I click the "+"
Button
, it toggles ALL student's grades on/off. What I want is to toggle the Grades
of only the student whose button I clicked.
Here's my code:
import axios from 'axios';
import { useState, useEffect } from 'react';
import styles from "./Home.module.css";
import { v4 as uuidv4 } from 'uuid';
const Home = () => {
const [students, setStudents] = useState([]);
const [filteredStudents, setFilteredStudents] = useState([]);
const [isShown, setIsShown] = useState(true);
const fetchStudents = async () => {
const response = await axios.get(`https://api.hatchways.io/assessment/students`);
setStudents(response.data.students);
setFilteredStudents(response.data.students);
console.log(response.data.students);
}
const findAverageGrade = arr => {
let sum = 0;
for (let i = 0; i < arr.length; i++) {
sum += parseInt(arr[i]);
}
return sum / arr.length;
}
const searchStudentName = async (searchName) => {
const searchNameFiltered = searchName.toLowerCase();
console.log(searchNameFiltered);
if (searchNameFiltered === "") {
fetchStudents();
return;
}
var newArray = await students.filter((student) => {
return student.firstName.toLowerCase().includes(searchNameFiltered)
|| student.lastName.toLowerCase().includes(searchNameFiltered);
})
await setFilteredStudents(newArray);
}
const toggleGrades = () => {
console.log("toggle");
}
useEffect(() => {
fetchStudents();
}, [])
return(
<>
<div>
<input type="text" placeholder="Search by name" onChange={(event) => searchStudentName(event.target.value) }/>
{filteredStudents.map((student) => (
<div key={student.email} className={styles.studentItem}>
<img className={styles.studentImage} src={student.pic} />
<div className={styles.studentInfoContainer}>
<div className={styles.studentHeader}>
<p className={styles.studentName}>{student.firstName} {student.lastName}</p>
</div>
<ul className={styles.studentDetail}>
<li>Email: {student.email}</li>
<li>Company: {student.company}</li>
<li>Skill: {student.skill}</li>
<li>Average: {findAverageGrade(student.grades)}%</li>
<button onClick={() => {
setIsShown(!isShown);
}}>
+
</button>
{isShown ? <div>
<table className={styles.gradesTable}>
<tbody>
{student.grades.map((grade) => (
<tr key={uuidv4()}>
<td>Test</td>
<td>{grade}%</td>
</tr>
))}
</tbody>
</table>
</div>
: null }
</ul>
</div>
</div>
)
)}
</div>
</>
)
}
export default Home;
Just remove the mapping part to another component and import it in map part. In this way you will have still same structure and it will work without any extra logic
NEW COMPONENT
const Students = ({student}) => {
const [isShown, setIsShown] = useState(true);
return (
<div key={student.email} className={styles.studentItem}>
<img className={styles.studentImage} src={student.pic} />
<div className={styles.studentInfoContainer}>
<div className={styles.studentHeader}>
<p className={styles.studentName}>{student.firstName} {student.lastName}</p>
</div>
<ul className={styles.studentDetail}>
<li>Email: {student.email}</li>
<li>Company: {student.company}</li>
<li>Skill: {student.skill}</li>
<li>Average: {findAverageGrade(student.grades)}%</li>
<button onClick={() => {
setIsShown(!isShown);
}}>
+
</button>
{isShown ? <div>
<table className={styles.gradesTable}>
<tbody>
{student.grades.map((grade) => (
<tr key={uuidv4()}>
<td>Test</td>
<td>{grade}%</td>
</tr>
))}
</tbody>
</table>
</div>
: null }
</ul>
</div>
</div>
)
}
HOME COMPONENT
import axios from 'axios';
import { useState, useEffect } from 'react';
import styles from "./Home.module.css";
import { v4 as uuidv4 } from 'uuid';
const Home = () => {
const [students, setStudents] = useState([]);
const [filteredStudents, setFilteredStudents] = useState([]);
const fetchStudents = async () => {
const response = await axios.get(`https://api.hatchways.io/assessment/students`);
setStudents(response.data.students);
setFilteredStudents(response.data.students);
console.log(response.data.students);
}
const findAverageGrade = arr => {
let sum = 0;
for (let i = 0; i < arr.length; i++) {
sum += parseInt(arr[i]);
}
return sum / arr.length;
}
const searchStudentName = async (searchName) => {
const searchNameFiltered = searchName.toLowerCase();
console.log(searchNameFiltered);
if (searchNameFiltered === "") {
fetchStudents();
return;
}
var newArray = await students.filter((student) => {
return student.firstName.toLowerCase().includes(searchNameFiltered)
|| student.lastName.toLowerCase().includes(searchNameFiltered);
})
await setFilteredStudents(newArray);
}
const toggleGrades = () => {
console.log("toggle");
}
useEffect(() => {
fetchStudents();
}, [])
return(
<>
<div>
<input type="text" placeholder="Search by name" onChange={(event) => searchStudentName(event.target.value) }/>
{filteredStudents.map((student) => (
<Student student={student} />
)
)}
</div>
</>
)
}
export default Home;
Note: I did not include export import parts of component and css but it's the easy part
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.