简体   繁体   中英

How can I toggle the display of an array that is located within each object of another array that comes from an API?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM