First time poster, so I apologize if I don't do things correctly. I am also new to React so bare with me, I will do my best to describe the problem.
I have a dataset that consists of school classes in json format. I am using axios to grab the data. The data consists of an array of objects which contains information about each class. I want to break up the classes so that they are separated into 4 different arrays based on what school year you are in (freshman, sophomore, junior, senior). I then want to display these classes on my web app.
Here is the code below:
import React from "react";
import { useState, useEffect } from "react";
import axios from "axios"
function Classes() {
useEffect(() => {
fetchClassObj();
},[]);
const [freshman, setFreshman] = useState([]);
const [sophomore, setSophomore] = useState([]);
const [junior, setJunior] = useState([]);
const [senior, setSenior] = useState([]);
const fetchClassObj = () => {
axios.get('/courses').then((res) => {
console.log(res);
res.data.forEach(item => {
if (item.number.includes('ECE 1')) {
freshman.push(item);
}
else if (item.number.includes('ECE 2')) {
sophomore.push(item);
}
else if (item.number.includes('ECE 3')) {
junior.push(item);
}
else if (item.number.includes('ECE 4')) {
senior.push(item);
}
});
setFreshman(freshman);
setSophomore(sophomore);
setJunior(junior);
setSenior(senior);
}).catch(error => {
console.log("Error Fetching Data", error)
})
}
return (
<>
<section>
<div >
<div >
<div className="classDes">
1000 level classes
</div>
<div onl oad="fetchClassObj()">
{freshman ?
freshman.map(freshman => {
return(
<div className="class-box">
<a href={'/classes/' freshman.number.replace(/ /g,'')}>{freshman.number} {freshman.name}</a>
</div>
)
}) : <h3>No data yet</h3> }
</div>
</div>
<div >
<div className="classDes">
2000 level classes
</div>
<div >
{sophomore ?
sophomore.map(sophomore => {
return(
<div className="class-box">
<a href={'/classes/' sophomore.number.replace(/ /g,'')}>{sophomore.number} {sophomore.name}</a>
</div>
)
}) : <h3>No data yet</h3> }
</div>
</div>
<div >
<div className="classDes">
3000 level classes
</div>
<div >
{junior ?
junior.map(junior => {
return(
<div className="class-box">
<a href={'/classes/' junior.number.replace(/ /g,'')}>{junior.number} {junior.name}</a>
</div>
)
}) : <h3>No data yet</h3> }
</div>
</div>
<div >
<div className="classDes">
4000 level classes
</div>
<div >
{senior ?
senior.map(senior => {
return(
<div className="class-box">
<a href={'/classes/' senior.number.replace(/ /g,'')}>{senior.number} {senior.name}</a>
</div>
)
}) : <h3>No data yet</h3> }
</div>
</div>
</div>
</section>
</>
)
}
export default Classes
{
"ece1004":
{
"number":"ECE 1004",
"name": "Introduction to ECE Concepts",
"des": "Introduction to topics that span the field of electrical and computer engineering (ECE). Content presented through the lens of application with accompanying hands-on exercises. Basics of circuits, op-amps, power supplies, computer logic, system decomposition, and coding. Modeling and application of engineering professionalism. Exploration of ECE in society.",
"pre": [["ENGE 1215", "ECE 1414"]],
"co" : [],
"minGrade": "C",
"offering": ["Fall", "Spring", "Summer"]
},
"ece2024":
{
"number":"ECE 2024",
"name": "Circuits and Devices",
"des": "Analysis and design of passive and active circuits under Direct Current (DC), Alternating Current (AC), and switched excitation. Linear circuit analysis techniques for various circuit topologies. Expressing the transient response of first- and second-order linear circuits using time-domain methods. Calculating the AC steady-state response of linear circuits using phasors and immittances. Characterizing the frequency response of linear circuits. Determining operating point and small signal response of non-linear circuits containing diodes and bipolar transistors. Projects demonstrating circuit design processes adhering to professional practices.",
"pre": ["ECE 1004", ["MATH 2114","MATH 2114H","MATH 2405H"]],
"co" : ["ECE 2514","ECE 2514", "ECE 2544", "MATH 2214", "PHYS 2306"],
"minGrade": "C",
"offering": ["Fall", "Spring"]
},
"ece2214":
{
"number":"ECE 2214",
"name": "Physical Electronics",
"des": "Fundamentals of electrostatics and magnetostatics, transmission lines, impedance matching networks, electromagnetic (EM) waves, and basic operating principles of diodes and metal-oxide semiconductor field-effect transistors (MOSFETs). Designing MOSFET biasing, and single-ended and differential amplifier circuits. Basic operating principles of complementary metal-oxide semiconductor (CMOS) device and its application as a digital inverter. Electronic circuit design adhering to professional and ethical practices.",
"pre": ["ECE 2024"],
"co" : [],
"minGrade": "C",
"offering": ["Fall", "Spring", "Summer"]
},
"ece2514":
{
"number":"ECE 2514",
"name": "Computational Engineering",
"des": "Software development processes for electrical and computer engineering applications. Modeling, simulation, data analysis, and visualization. Computing abstractions and the use of application programming interfaces. Software design and implementation using a procedural, class-based language. Integrated code development and testing. Team-based development of autonomous system applications reinforcing course topics.",
"pre": ["ECE 1004"],
"co" : ["ECE 2024", "ECE 2544"],
"minGrade": "C",
"offering": ["Fall", "Spring"]
}
}
Here is an example of the json data that I am pulling from axios. res.data
gives an array of objects that contain the information above.
Each class object has a name member. The class names consist of things like ECE 2024, ECE 3074 etc. 1000 corresponds to freshman, 2000 sophomore, 3000 junior, 4000 senior.
When outputting to console, the freshman-senior arrays populate correctly. However, When first navigating to the page, the only thing that shows up are the "1000-4000 level classes" div. When I modify something in the fetchClassObj()
function, everything shows up as I expected it to.
Whenever I try do not break up the classes into their respective levels, everything outputs correctly. So, I think this has to something to do with how I break up my data, or a race condition with the useEffect()
function, but I can't seem to figure out what is wrong. Any help would be appreciated. Thank you!
CodePudding user response:
You must not modify the states directly as you are trying with freshman.push(item)
, instead use setFreshman([...freshman, item])
CodePudding user response:
There are some things that can be improved in your code which will at the same time solve your issue. One issue is that you do modify the state directly using push()
. State should be treated as immutable so create a new array instanc with that value instead using setState([...myArray, newValue])
.
But considering you only set the state once and you get all the data at once from the API, why have 4 different states that will cause a re-render when you have to update them at the same time anyway. You can put them in one JS object and you'll only have to update the state once, triggering just one re-render. As an added benefit you can get rid of the lengthy if
statements as well when using a JS object and reduce()
. You can use e.g. the year a student is in as a unique key, as every element in the array has that key and use that to assign each student in their corresponding year. This will make your code far more readable and easily extendable than all the ifs.
Following the DRY principle you should factor out the logic to display a class/ year of students into another component and reuse that component. Again, a whole lot of readability and maintainability is gained.
Here is my implementation of your component applying all the stuff I have mentioned and as you can see it's quite a bit easier to read and maintain. Additionally all the state update errors you have are gone.
To mock the delay of the API request I used
setTimeout()
before setting the data.// example data const data = { ece1004: { number: "ECE 1004", name: "Introduction to ECE Concepts", des: "Introduction to topics that span the field of electrical and computer engineering (ECE). Content presented through the lens of application with accompanying hands-on exercises. Basics of circuits, op-amps, power supplies, computer logic, system decomposition, and coding. Modeling and application of engineering professionalism. Exploration of ECE in society.", pre: [["ENGE 1215", "ECE 1414"]], co: [], minGrade: "C", offering: ["Fall", "Spring", "Summer"], }, ece2024: { number: "ECE 2024", name: "Circuits and Devices", des: "Analysis and design of passive and active circuits under Direct Current (DC), Alternating Current (AC), and switched excitation. Linear circuit analysis techniques for various circuit topologies. Expressing the transient response of first- and second-order linear circuits using time-domain methods. Calculating the AC steady-state response of linear circuits using phasors and immittances. Characterizing the frequency response of linear circuits. Determining operating point and small signal response of non-linear circuits containing diodes and bipolar transistors. Projects demonstrating circuit design processes adhering to professional practices.", pre: ["ECE 1004", ["MATH 2114", "MATH 2114H", "MATH 2405H"]], co: ["ECE 2514", "ECE 2514", "ECE 2544", "MATH 2214", "PHYS 2306"], minGrade: "C", offering: ["Fall", "Spring"], }, ece2214: { number: "ECE 2214", name: "Physical Electronics", des: "Fundamentals of electrostatics and magnetostatics, transmission lines, impedance matching networks, electromagnetic (EM) waves, and basic operating principles of diodes and metal-oxide semiconductor field-effect transistors (MOSFETs). Designing MOSFET biasing, and single-ended and differential amplifier circuits. Basic operating principles of complementary metal-oxide semiconductor (CMOS) device and its application as a digital inverter. Electronic circuit design adhering to professional and ethical practices.", pre: ["ECE 2024"], co: [], minGrade: "C", offering: ["Fall", "Spring", "Summer"], }, ece2514: { number: "ECE 2514", name: "Computational Engineering", des: "Software development processes for electrical and computer engineering applications. Modeling, simulation, data analysis, and visualization. Computing abstractions and the use of application programming interfaces. Software design and implementation using a procedural, class-based language. Integrated code development and testing. Team-based development of autonomous system applications reinforcing course topics.", pre: ["ECE 1004"], co: ["ECE 2024", "ECE 2544"], minGrade: "C", offering: ["Fall", "Spring"], }, }; function Classes() { React.useEffect(() => { fetchClassObj(); }, []); // this object will contain one array for each year const [classes, setClasses] = React.useState({}); const fetchClassObj = () => { // immitate network delay setTimeout(() => { // get values of the object in an array // now assign each student to the year he is in using reduce() const classMapping = [...Object.values(data)].reduce( (allStudents, student) => { // use substring to get the year from the string and then use the year to get the appropriate array in the object allStudents[student.number.substring(4, 5)].push(student); return allStudents; }, // initial value: years with empty array; use the year as a key here (could be something different like the whole class name as well) { 1: [], 2: [], 3: [], 4: [] } ); // now we only need to update one state // as you always update all the states together this makes more sense than having them in 4 different states setClasses(classMapping); }, 2000); }; return ( <section> <div > {Object.entries(classes).map(([year, students]) => ( <Class level={year "000"} students={students}></Class> ))} </div> </section> ); } function Class({ level, students }) { return ( <div > <div className="classDes">{`${level} level classes`}</div> <div > {students.length !== 0 ? ( students.map((student) => ( <div className="class-box"> <a href={"/classes/" student.number.replace(/ /g, "")}> {student.number} {student.name} </a> </div> )) ) : ( <div>No data yet</div> )} </div> </div> ); } ReactDOM.render(<Classes />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script> <div id="root"></div>