Sum of an array from json data - javascript

hi guys im having trouble bringing back the sum of an array from a json api.
im able to select the json data and select the grades only. my plan was ... once i have the grades to use the reduce method to get the sum of each array and then divide by the length. so as you can see it didnt work out , infact it brings me some strange number which looks like the total of all the numbers in each array divided by the length of each array. i tried every single way to do it for loop etc . its not the for loop or the method im using that doesnt work, its the way im bringing down the array or atleast the way my reduce method is calculating the arrays. so how do i get the sum of each array individually? theres 25 arrays so ill need 25 sums. i placed the code down here below. or above ...
import React, { useEffect, useState } from "react";
import Card from "#material-ui/core/Card";
import CardContent from '#material-ui/core/CardContent';
import Grid from "#material-ui/core/Grid";
import { Input } from "#material-ui/core";
function StudentProfiles() {
const [info, setInfo] = useState();
const [input, setInput] = useState('');
const [filtered, setFiltered] = useState([]);
const [Average, setAverage] = useState([]);
useEffect(() => {
fetch("https://api.hatchways.io/assessment/students")
.then(response => response.json())
.then(json => setInfo(json.students))
},[]);
const filterChange = (e) => {
e.preventDefault();
const target = e.target.value;
setInput(target);
if(input !== '') {
const result = info && info.filter((user) => {
return user.firstName.toLowerCase().startsWith(input.toLowerCase());
});
setFiltered(result);
} if(input !== '') {
const result = info && info.filter((person) => {
return person.lastName.toLowerCase().startsWith(input.toLowerCase());
});
setFiltered(result);
} else {
setFiltered(info);
}
}
const gradePopulate = () => {
info && info.map((grades) => {
const listGrades = grades.grades;
setAverage(listGrades);
var a=0;
const p = Average.reduce((a,b) => a + b , a);
console.log(p);
});
}
return (
<div>
<Card className="card">
<CardContent className="scrollbar scrollbar-primary mt-5 mx-auto">
<Input
className="searchBar"
icon="search"
placeholder="Search by name"
onChange={filterChange}
value={input}
/>
{filtered.map((name) => (
<ul className = "border" key={name.id}>
<Grid item xs={3} sm={6} md={12} style={{display: "flex", gap:"3.5rem", paddingBottom:"8px"}}>
<img alt ="" src={name.pic} className="picture"></img>
<Grid container style={{display: "inline"}} align="left" justifyContent="flex-end" alignItems="flex-start">
<Grid className="studentNames">
<span>{name.firstName + " " + name.lastName}</span>
<Grid container align="right" justifyContent="flex-end" alignItems="flex-end" style={{display: "inline"}} >
<button onClick={gradePopulate} className="plus"></button></Grid>
</Grid>
<span>{name.email}</span>
<br/>
<span>{name.company}</span>
<br/>
<span>{name.skill}</span>
<br/>
<span>Average: {9}%</span>
</Grid>
</Grid>
</ul>
))}
</CardContent>
</Card>
</div>
)
}
export default StudentProfiles;

I don't understand how your data is structured but from your code, I am guessing
info = [{ grades: [] }, { grades: [] }]
and you want to sum the grades in each objects. If so, you can change your function to the below.
const store = {students: [{grades: ["78","100","92","86","89","88","91","87"]}, {grades: ["75","89","95","93","99","82","89","76"]}, {grades: ["88","90","79","82","81","99","94","73"]}, {grades: ["88","93","92","81","95","98","77","94"]}, {grades: ["71","81","72","92","79","82","91","90"]}, {grades: ["97","92","72","99","92","92","79","96"]}]};
const info = store.students
const gradePopulate = () => {
return info && info.map((grades) => {
const listGrades = grades.grades;
// setAverage(listGrades);
const p = listGrades.reduce((a,b) => a + Number(b) , 0);
return p
});
}
console.log(gradePopulate())
const store = {students: [{grades: ["78","100","92","86","89","88","91","87"]}, {grades: ["75","89","95","93","99","82","89","76"]}, {grades: ["88","90","79","82","81","99","94","73"]}, {grades: ["88","93","92","81","95","98","77","94"]}, {grades: ["71","81","72","92","79","82","91","90"]}, {grades: ["97","92","72","99","92","92","79","96"]}]};
const info = store.students
const gradePopulate = () => {
const totalSum = info.reduce((a, b) => a + b.grades.reduce((c, d) => c + Number(d), 0), 0);
const totalLength = info.reduce((a, b) => a + b.grades.length, 0)
return totalSum / totalLength
}
console.log(gradePopulate())

Related

How Can I filter and remove title Categories from array of array?

I have a list separated by alphabetical categories. All that start with the letter A are grouped, letter B are grouped and so on.
When I filter this list, the titles corresponding to each letter do not disappear. The filter is working, but the screen is loaded with the titles of the letters.
When I make the filter, can you tell me how I can remove a letter category that is not being used?
Here's my code I put into codesandbox
Thank you for any help.
import React from "react";
import "./styles.css";
import { data } from "./data";
export default function App() {
const [filter, setFilter] = React.useState("");
const _data = Object.values(
data.reduce((c, e) => {
if (!c[e.id]) {
c[e.id] = e;
}
return c;
}, {})
);
const merged = [];
_data.map((arr) => arr.map((item) => merged.push(item)));
const mapped = merged.reduce((acc, item) => {
const letter = item.title[0].toLowerCase();
if (!acc[letter]) {
acc[letter] = [];
}
acc[letter].push(item);
return acc;
}, {});
const letters = Object.keys(mapped);
return (
<div className="App">
<h1>Grouped by the first initial</h1>
<div>
<input value={filter} onChange={(e) => setFilter(e.target.value)} />
</div>
{letters.map((letter, i) => (
<div key={i}>
<h2>{letter}</h2>
{mapped[letter]
.filter(
(inputFilter) =>
inputFilter.title.toLowerCase().indexOf(filter.toLowerCase()) >=
0
)
.map((item, j) => (
<div key={j}>{item.title}</div>
))}
</div>
))}
</div>
);
}
You could show letter if and even if filter search got result:
So letter visibility becomes:
{letterIsVisible(letter) && <h2>{letter}</h2>}
where letterIsVisible is a function like:
const letterIsVisible = (letter) => {
return (
mapped[letter].filter(
(inputFilter) =>
inputFilter.title.toLowerCase().indexOf(filter.toLowerCase()) >= 0
).length > 0
);
};
Here your codesandbox modified.

How to search innerHTML in react

I have a list of users on the page.
Each student has an input filed where user can add tags to their profile. There's a search bar on top of the all the students, searchStudentByTags. I am trying to implement this function, but have not been able to solve it yet. Any help would be appreciated.
This is the StudentContainer component where has the searchStudnetByTags function I write so far but not working
import React, { useState, useMemo } from "react";
import Student from "./Student";
import Input from "./Input";
import "../stylesheets/StudentsContainer.scss";
const StudentsContainer = ({ students }) => {
const [searchByName, setSearchByName] = useState("");
const [searchByTags, setSearchByTags] = useState("");
const filteredStudents = useMemo(
() =>
students.filter(
({ firstName, lastName }) =>
searchByName.length < 2 ||
(firstName + " " + lastName)
.toLowerCase()
.includes(searchByName.toLowerCase())
),
[students, searchByName]
);
const renderStudentsByTagSearch = ({ target }) => {
setSearchByTags(target.value);
const studentsContainer = document.querySelector(".students-container");
const allStudents = studentsContainer.getElementsByClassName("student");
const nameTags = document.querySelectorAll(".tag");
for (let i = 0; i < allStudents.length; i++) {
const student = allStudents[i];
const tag = nameTags[i];
if (
searchByTags.length > 1 &&
student.contains(tag) &&
tag.innerHTML.includes(searchByTags)
) {
student.style.display = "";
} else if (
searchByTags.length > 1 &&
student.contains(tag) &&
!tag.innerHTML.includes(searchByTags)
) {
student.style.display = "none";
} else if (searchByTags.length > 1 && !student.contains(tag)) {
student.style.display = "none";
} else if (searchByTags.length === 0 || !student.contains(tag)) {
student.style.display = "";
}
}
};
return (
<section className="students-container">
<Input
value={searchByName}
placeholder="Search by name"
onChange={({ target }) => setSearchByName(target.value)}
/>
<Input
className="tag-input"
value={searchByTags}
placeholder="Search by tag"
onChange={renderStudentsByTagSearch}
/>
{filteredStudents.map((student) => (
<Student
key={student.id}
student={student}
/>
))}
</section>
);
};
export default StudentsContainer;
This is the Student component
import React, { useState } from "react";
import "../stylesheets/Student.scss";
import AddTag from "./AddTag";
const Student = ({ student, addTagClick }) => {
const averageGrade =
student.grades.reduce((acc, grade) => {
return parseInt(acc) + parseInt(grade);
}) / student.grades.length;
const [isViewScores, setIsViewScores] = useState(false);
const viewScoreClick = () => {
setIsViewScores((prev) => !prev);
};
return (
<article className="student">
<figure>
<img src={student.pic} alt="student" />
</figure>
<aside>
<h2>
{student.firstName} {student.lastName}
</h2>
<ul>
<li>Email: {student.email}</li>
<li>Company: {student.company}</li>
<li>Skill: {student.skill}</li>
<li>
Average: {averageGrade}%
{isViewScores && (
<ul className="scores">
{student.grades.map((grade, index) => {
return (
<li key={index}>
Test {index + 1}: {grade}%
</li>
);
})}
</ul>
)}
</li>
</ul>
<AddTag studentId={student.id} addTagClick={addTagClick}/>
</aside>
<button onClick={viewScoreClick} className="view-scores-btn">
{isViewScores ? "-" : "+"}
</button>
</article>
);
};
export default Student;
This is the AddTag component
import React, { useState } from "react";
import { generateId } from "../helper";
import Input from "./Input";
const AddTag = ({ studentId }) => {
const [tag, setTag] = useState("");
const [tags, setTags] = useState([]);
const handleInputChange = ({ target }) => {
setTag(target.value);
};
const onSubmitClick = (e) => {
e.preventDefault();
const newTag = {
tag: tag,
id: generateId(),
studentId: studentId,
};
setTags((prev) => {
if (tag) {
return [newTag, ...prev];
} else {
return [...prev];
}
});
setTag("");
};
return (
<>
<div className="tags-container">
{tags.map((tag) => (
<button className="tag" key={tag.id}>
{tag.tag}
</button>
))}
</div>
<form onSubmit={onSubmitClick}>
<Input
className="add-tag-input"
placeholder="Add a tag"
type="text"
value={tag}
onChange={handleInputChange}
/>
</form>
</>
);
};
export default AddTag;
You need to approach this differently.. where the array of tags are available at the top level component - rather than doing DOM manipulation. Move
const [tags, setTags] = useState([]);
Into the StudentsContainer, and pass it down through Students and Add Tag as props, then refactor your search to use tags.
I've added a code sandbox here, with a basic gist of how I'd approach it.
https://codesandbox.io/s/frosty-ishizaka-hui8j
Theres quite a bit going in this question so we should focus on simplifying the problem by removing everything that is of no concern.
So how do we only render those students who have the tag that we currently are searching for? By using Array.prototype.filter() before we map over students and return a <Student /> for each array item.
import React, { useState } from "react";
const data = [
{id:1,firstName:"Mickey",lastName:"Mouse",tags:[{id:1,label:"mouse"}]},
{id:2,firstName:"Donald",lastName:"Duck",tags:[{id:1,label:"duck"}]},
{id:3,firstName:"Minnie",lastName:"Mouse",tags:[{id:1,label:"mouse"},{id:2,label:"cool"}]}
];
const StudentsContainer = ({ students = data }) => {
const [searchByTagsValue, setSearchByTagsValue] = useState("");
return (
<>
<input
value={searchByTagsValue}
placeholder="Search by tag"
onChange={(e) => setSearchByTagsValue(e.target.value)}
/>
{students.length &&
students
.filter((student) => shouldStudentDisplay(student.tags, searchByTagsValue))
.map((student) => <Student key={student.id} student={student} />)}
</>
);
};
const Student = ({ student, style }) => (
<div style={style}>
<h5>
{student.firstName} {student.lastName}
</h5>
<Tags tags={student.tags} />
<hr />
</div>
);
const Tags = ({ tags }) => (
<ul>
{tags.map((tag) => (
<li key={tag.id}>{tag.label}</li>
))}
</ul>
);
const shouldStudentDisplay = (tags, searchByTagsValue) => {
if (!searchByTagsValue) {
return true;
}
return tags.findIndex(({ label }) => label === searchByTagsValue) !== -1;
};
export default StudentsContainer;
Once you can filter your data in place like above, you need an updater function in StudentsContainer that will take a student id, and a new tag name, and update (a localised version of) the students data.
Pass this updater function all the way from StudentsContainer down to Tags so it can update the data in the ancestor component (commonly referred to as prop drilling).
const [localStudents, setLocalStudents] = useState(students);
const onSubmitTag = (label, id) => {
const index = localStudents.findIndex((student) => student.id === id);
if (index !== -1) {
const newStudents = [...localStudents];
newStudents[index] = {
...newStudents[index],
tags: [...newStudents[index].tags, { id: Date.now(), label }]
};
setLocalStudents(newStudents);
}
};
As you can see, we aren't really searching through the HTML to hide and show things in an imperative way.
In react, we are encouraged to update the source data, and allow the rendered UI to react in a declarative way.
React makes it painless to create interactive UIs. Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes.
Declarative views make your code more predictable and easier to debug.

React App becomes blank when I try to update an array in the state

I am trying to manipulate the images shown in my React App by voice. I implemented the SR, which works fine:
<button onClick={SpeechRecognition.startListening}>Start</button>
I have an array of loadImages in my state, which is empty first:
const [loadImages, setLoadImages] = React.useState([]);
Whenever the word spoken is "kitten", the array of loadImages gets updated in this way:
if(transcript == "kitten")
{
const newImages = loadImages.concat({image: 'https://www.onlinekittencare.com/wp-content/uploads/2020/07/vChK6pTy3vN3KbYZ7UU7k3-1200-80.jpg'})
setLoadImages(newImages);
}
The transcript you see is a variable, which I initialized here:
const {transcript} = useSpeechRecognition();
In the render I use it to show what the SR understood, so if I say "hello" it shows "hello":
<p id="transcript">Transcript: {transcript}</p>
And this is where the images in loadImages show up:
{images.map((image) => {
return <URLImage image={image}/>;
})}
The problem is that whenever I say "kitten", which as stated above is used as a command to add the picture to the array loadImages, my React App gets blank. In the inspect I can also see that it says react-dom.development.js:14997 Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
How do I fix that?
EDIT (I added the whole code):
function App() {
const [rectangles, setRectangles] = useState([]);
const [circles, setCircles] = useState([]);
const [selectedId, selectShape] = useState(null);
const [shapes, setShapes] = useState([]);
const [, updateState] = React.useState();
const stageEl = React.createRef();
const layerEl = React.createRef();
const fileUploadEl = React.createRef();
const [brushSize, setBrushSize] = React.useState('5');
const [isDrawing, setIsDrawing] = React.useState(false);
const dragUrl = React.useRef();
//const stageRef = React.useRef();
const [images, setImages] = React.useState([]);
const [loadImages, setLoadImages] = React.useState([]);
const getRandomInt = max => {
return Math.floor(Math.random() * Math.floor(max));
};
const {transcript} = useSpeechRecognition();
const URLImage = ({image}) => {
const [img] = useImage(image.src);
return (
<Image
image = {img}
x = {image.x}
y = {image.y}
offsetX = {50}
offsetY = {50}
width={200}
height={200}
draggable
/>
);
};
const drawLine = () => {
setIsDrawing(true);
if(isDrawing){
addLine(stageEl.current.getStage(), layerEl.current, brushSize);
};
};
const eraseLine = () => {
addLine(stageEl.current.getStage(), layerEl.current, brushSize, "erase");
};
const addRectangle = () => {
setIsDrawing(false);
const rect = {
x: getRandomInt(100),
y: getRandomInt(100),
width: 100,
height: 100,
fill: "red",
id: `rect${rectangles.length + 1}`,
};
const rects = rectangles.concat([rect]);
setRectangles(rects);
const shs = shapes.concat([`rect${rectangles.length + 1}`]);
setShapes(shs);
};
const forceUpdate = React.useCallback(() => updateState({}), []);
const undo = () => {
const lastId = shapes[shapes.length - 1];
let index = circles.findIndex(c => c.id == lastId);
if (index != -1) {
circles.splice(index, 1);
setCircles(circles);
}
index = rectangles.findIndex(r => r.id == lastId);
if (index != -1) {
rectangles.splice(index, 1);
setRectangles(rectangles);
}
index = images.findIndex(r => r.id == lastId);
if (index != -1) {
images.splice(index, 1);
setImages(images);
}
shapes.pop();
setShapes(shapes);
forceUpdate();
};
document.addEventListener("keydown", ev => {
if (ev.code == "Delete") {
let index = circles.findIndex(c => c.id == selectedId);
if (index != -1) {
circles.splice(index, 1);
setCircles(circles);
}
index = rectangles.findIndex(r => r.id == selectedId);
if (index != -1) {
rectangles.splice(index, 1);
setRectangles(rectangles);
}
index = images.findIndex(r => r.id == selectedId);
if (index != -1) {
images.splice(index, 1);
setImages(images);
}
forceUpdate();
}
});
if(transcript == "kitten")
{
const newImages = loadImages.concat({image: 'https://www.onlinekittencare.com/wp-content/uploads/2020/07/vChK6pTy3vN3KbYZ7UU7k3-1200-80.jpg'})
setLoadImages(newImages);
}
return (
<div className="home-page">
{loadImages.map(image => (
<img id="img" className="img"
src={image.image}
width="200"
height="200"
onDragStart={(e) => {
dragUrl.current = e.target.src;}}
/>
))}
<div
onDrop={(e) => {
e.preventDefault();
// register event position
stageEl.current.setPointersPositions(e);
// add image
setImages(
images.concat([
{
...stageEl.current.getPointerPosition(),
src: dragUrl.current,
},
])
);
}}
onDragOver={(e) =>
e.preventDefault()
}
>
<h1>Whiteboard</h1>
<button onClick={addRectangle}>
Rectangle
</button>
<button>
Circle
</button>
<button onClick={drawLine}>
Line
</button>
<button onClick={eraseLine}>
Erase
</button>
<select
value={brushSize}
onChange={(e) => {
setBrushSize(e.target.value);
drawLine();
}}
>
<option value="5">5</option>
<option value="20">20</option>
<option value="50">50</option>
</select>
<button variant="secondary">
Text
</button>
<button variant="secondary">
Image
</button>
<button variant="secondary" onClick={undo}>
Undo
</button>
<p id="transcript">Transcript: {transcript}</p>
<button onClick={SpeechRecognition.startListening}>Start</button>
<Stage
width={window.innerWidth * 0.9}
height={window.innerHeight - 150}
ref={stageEl}
dragabble
onMouseDown={e => {
// deselect when clicked on empty area
const clickedOnEmpty = e.target === e.target.getStage();
if (clickedOnEmpty) {
selectShape(null);
}
}}
>
<Layer ref={layerEl}>
{rectangles.map((rect, i) => {
return (
<Rectangle
key={i}
shapeProps={rect}
isSelected={rect.id === selectedId}
//onSelect={() => {
// selectShape(rect.id);
//}}
onChange={newAttrs => {
const rects = rectangles.slice();
rects[i] = newAttrs;
setRectangles(rects);
}}
/>
);
})}
{images.map((image) => {
return <URLImage image={image}/>;
})}
</Layer>
</Stage>
</div>
</div>
);
}
export default App;
Based on the code you've shared, it has to do with how you're updating the state if the transcript is equal to kitten.
Essentially, the logic you've written says, on render, if the transcript is kitten, update the state. BUT, when you update the state, that will re-render, and hit that logic again... and again... and again. The solution here is to wrap that in a useEffect – React Docs explain it best but in simple terms, you want to "do something" as a side effect of "something else".
In this case, if the transcript updates, you want to check the state of transcript, and if it meets a condition, you want to update your state:
React.useEffect(() => {
if (transcript === "kitten") {
const newImages = loadImages.concat({image: 'https://www.onlinekittencare.com/wp-content/uploads/2020/07/vChK6pTy3vN3KbYZ7UU7k3-1200-80.jpg'})
setLoadImages(newImages);
}
}, [transcript]);
The final piece to the useEffect is a dependency array ([transcript])–this dictates to React which item you want to watch for changes in–if transcript changes, it will run your effect and only when it changes, instead of every time it renders.

Send values from inputs in react (change querySelector(id) to react)

I am trying to rewrite a small app from vanilla js to react, and in one element I encountered a problem with passing on values in the inputs. What this element does, is after selecting a number it generates that many inputs to fill, and after filling send its id and value further (value can also be empty)
In Vanilla Js I did it with id and querySelector, but in React I have a trouble to change it correct
React code:
import React, { useState, useEffect } from "react";
import "./style.css";
import Values from "./Values";
export default function App() {
const [numberValue, setNumberValue] = useState("");
const [inputValues, setInputValues] = useState([]);
const [sendValues, setSendValues] = useState(false);
const [inputs, setInputs] = useState([]);
let numbers = [4, 6, 8];
//reset teamsName on change teamsValue
useEffect(() => {
for (let i = 1; i <= numberValue; i++) {
setInputValues(prev => [
...prev,
{
id: i,
value: ""
}
]);
}
}, [numberValue]);
const showButtons = numbers.map((number, i) => (
<button
className={`${numberValue === number ? "button active" : "button"}`}
onClick={() => {
setNumberValue(number);
setInputValues([]);
setInputs([]);
showInputs();
}}
>
{number}
</button>
));
//let inputs = [];
const showInputs = () => {
for (let i = 1; i <= numberValue; i++) {
setInputs(prev => [
...prev,
<input
type="text"
className="input"
placeholder={`Input ${i}`}
//value={inputValues.find(input => input.id === i && input.value)}
onChange={e =>
inputValues.filter(
input =>
input.id === i &&
setInputValues([
...inputValues,
{ id: i, value: e.target.value }
])
)
}
/>
]);
}
};
return (
<>
<div className="button-group">{showButtons}</div>
{numberValue && (
<>
<h3 className="title">Your inputs</h3>
<div className="input-group">{inputs}</div>
</>
)}
<button onClick={() => setSendValues(true)}>SEND</button>
{sendValues && <Values inputValues={inputValues} />}
</>
);
}
JS:
const buttonGroup = document.querySelector(".button-group");
const inputGroup = document.querySelector(".input-group");
const inputValues = document.querySelector(".input-values");
let n;
const showInputs = number => {
n = number;
inputGroup.innerHTML = ''
for (let i = 1; i <= number; i++) {
inputGroup.innerHTML += `
<input type="text" name="name" id="input-${i}" class="input" placeholder="team name"> <br>
`;
}
};
let values = []
const showValues = () => {
//clear
inputValues.innerHTML = '';
values = [];
//show new
for (let i = 1; i <= n; i++) {
const input_val = document.querySelector(`#input-${i}`).value;
values.push({
id: i,
value: input_val
});
}
for(let i = 0; i<=n; i++){
inputValues.innerHTML += `
<p>id: ${values[i].id} value:${values[i].value}
</p>
`
}
};
Links to code:
React -> https://stackblitz.com/edit/react-uw9dzc?file=src/App.js
JS -> https://codepen.io/Arex/pen/qBqLVBq?editors=1111
I took the liberty to simplify your code a bit. Basically I assigned value as it's own variable const value = e.target.value; as it is a synthetic event and tends to get lost if you pass it further down, so this preserves the value. Also, I changed inputValues to an object to make it easier to update:
// App.js
export default function App() {
const [numberValue, setNumberValue] = useState("");
const [inputValues, setInputValues] = useState({});
const [sendValues, setSendValues] = useState(false);
let numbers = [4, 6, 8];
const showButtons = numbers.map((number, i) => (
<button
className={`${numberValue === number ? "button active" : "button"}`}
onClick={async () => {
await setNumberValue(number);
await setInputValues({});
}}
>
{number}
</button>
));
return (
<>
<div className="button-group">{showButtons}</div>
{numberValue && (
<>
<h3 className="title">Your inputs</h3>
<div className="input-group">
{[...new Array(numberValue)].map((_value, id) => (
<input
type="text"
className="input"
placeholder={`Input ${id}`}
onChange={e => {
const value = e.target.value;
setInputValues(prev => {
prev[id] = value;
return prev;
});
}}
/>
))}
</div>
</>
)}
<button onClick={() => setSendValues(true)}>SEND</button>
{sendValues && <Values inputValues={inputValues} />}
</>
);
}
// Values.js
const Values = ({ inputValues }) => {
const showValues = Object.keys(inputValues).map(input => (
<div>
{input} : {inputValues[input]}
</div>
));
return <div>{showValues}</div>;
};
export default Values;
There are multiple issues in the shared code as follows:
It is not recommended to store components in state, in the shared code you are storing <input/> component in state. For more details check this
Unnecessary states are being used, always try to keep a minimum number of states as more number of states as more states are needed to be managed, making things unnecesarily complicated.
using previous state syntax to generate new state where it is not needed.
I am adding a working code with minimum changes for you reference.
App.js
import React, { useState, useEffect } from "react";
import "./style.css";
import Values from "./Values";
export default function App() {
const [numberValue, setNumberValue] = useState('');
const [inputValues, setInputValues] = useState([]);
const [sendValues, setSendValues] = useState(false);
let numbers = [4, 6, 8];
//reset teamsName on change teamsValue
useEffect(() => {
setInputValues(
Array(numberValue).fill("")
);
setSendValues(false)
}, [numberValue]);
const showButtons = numbers.map((number, i) => (
<button
className={`${numberValue === number ? "button active" : "button"}`}
onClick={() => {
setNumberValue(number);
}}
>
{number}
</button>
));
return (
<>
<div className="button-group">{showButtons}</div>
{numberValue && (
<>
<h3 className="title">Your inputs</h3>
<div className="input-group">
{inputValues.map((val, i) => (
<input
key={`input${i}`}
type="text"
className="input"
placeholder={`Input ${i+1}`}
value={val}
onChange={(e) => {let newValues = inputValues.slice(); newValues[i]=e.target.value; setInputValues(newValues)}
}
/>
))}
</div>
</>
)}
<button onClick={() => setSendValues(true)}>SEND</button>
{sendValues && <Values inputValues={inputValues} />}
</>
);
}
Values.js
import React from "react";
const Values = ({ inputValues }) => {
const showValues = inputValues.map((input, i) => (
<div key={'output'+i}>
{i+1} : {input}
</div>
));
return <div>{showValues}</div>;
};
export default Values;
I am also sharing a updated stackblitz code reference you shared for better understanding Updated snippet with fixes for reference.

How can I display my reversed array using the onClick event in React?

I pulled data from an API and mapped it but I am wanting to reverse the order when the user clicks MARKET CAP. I want the user to click the
<div>MARKET CAP</div>
Down in the code I am wanting to replace this array I am mapping:
{props.coins.filter(searchingFor(search)).map...
with the reversed one I made:
const [reverseCoin, setReverseCoin] = useState(
[...coin].filter(searchingFor(search)).reverse()
);
I have no idea how to replace the original data with the reversed one so any suggestions would be great. I'm using React, styled components and hooks. Here's my code:
import Button from "../../UI/Forms/Button/Button";
import styled from "styled-components";
import Icon from "../../assets/images/sort-solid.svg";
import SearchIcon from "../../assets/images/search-solid.svg";
import * as Styles from "../../components/Table/Tables.styles";
import { Link } from "react-router-dom";
import "./PriceList.scss";
function searchingFor(search) {
return function(x) {
return x.name.toLowerCase().includes(search.toLowerCase()) || false;
};
}
//MY FUCTIONAL COMPONENT*************
//one prop has been passed to this which I called it "coins"
const PriceList = props => {
console.log(props.coins);
const [coin, setCoin] = useState([]);
const [color, setColor] = useState("");
const [MarketCapLow, setMarketCapLow] = useState(false);
const [search, setSearch] = useState("");
/// this is the variable that holds the reversed array
const [reverseCoin, setReverseCoin] = useState(
[...coin].filter(searchingFor(search)).reverse()
);
const timeIntervels = ["1H", "24H", "1W", "1M", "1Y"];
useEffect(() => {
setCoin(props.coins);
}, [props.coins]);
const updateSearch = e => {
setSearch(e.target.value);
};
const handleClick = name => {
setColor(name);
};
//creating a table for my data********
return (
<TableContainer>
<SearchBarMainContainer>
<SearchBarContainer>
<SearchInputContainer>
<img
src={SearchIcon}
width="20"
height="20"
style={{ marginRight: "16px" }}
/>
<SearchBarInput
type="text"
value={search}
onChange={updateSearch}
placeholder="Search coins..."
/>
</SearchInputContainer>
<SearchPriceChange>
{timeIntervels.map(d => (
<SearchPriceChangeItems
id={d}
onClick={() => {
handleClick(d);
}}
className={color === d ? "purple" : "black"}
>
{d}
</SearchPriceChangeItems>
))}
</SearchPriceChange>
</SearchBarContainer>
</SearchBarMainContainer>
<Styles.Tablestyles>
<tbody>
<Styles.TableRowStyles bg>
<Styles.TabelHeadingStyles bg>#</Styles.TabelHeadingStyles>
<Styles.TabelHeadingStyles bg>NAME</Styles.TabelHeadingStyles>
<Styles.TabelHeadingStyles bg>PRICE</Styles.TabelHeadingStyles>
<Styles.TabelHeadingStyles bg>CHANGE</Styles.TabelHeadingStyles>
<Styles.TabelHeadingStyles bg>
<Styles.MarketCap
onClick={() => {
setMarketCapLow(!MarketCapLow);
}}
>
<div>MARKET CAP</div>
<CoinIcon width height src={Icon} />
</Styles.MarketCap>
</Styles.TabelHeadingStyles>
<Styles.TabelHeadingStyles bg>TRADE</Styles.TabelHeadingStyles>
</Styles.TableRowStyles>
{props.coins.filter(searchingFor(search)).map(coin => {
const {
rank,
logo_url,
name,
["1d"]: { price_change_pct },
currency,
price,
market_cap
} = coin;
const newMarketPct = (price_change_pct * 100).toFixed(2);
const newPrice = Math.floor(price * 100) / 100;
const newMarketCap =
Math.abs(market_cap) > 999999999
? Math.sign(market_cap) *
(Math.abs(market_cap) / 1000000000).toFixed(1) +
"B"
: Math.sign(market_cap) * Math.abs(market_cap);
return (
<Styles.TableRowStyles key={rank}>
<Styles.TabelDataStyles>{rank}</Styles.TabelDataStyles>
<Styles.TabelDataStyles grey flex>
<CoinIcon style={{ marginRight: "12px" }} src={logo_url} />
{name} ({currency})
</Styles.TabelDataStyles>
<Styles.TabelDataStyles>${newPrice}</Styles.TabelDataStyles>
<Styles.TabelDataStyles
style={
price_change_pct.charAt(0) === "-"
? { color: "#ff2734" }
: { color: "#23cc9a" }
}
>
{newMarketPct}%
</Styles.TabelDataStyles>
<Styles.TabelDataStyles>${newMarketCap}</Styles.TabelDataStyles>
<Styles.TabelDataStyles>
<Link to={`/prices/${coin.currency}`}>
<Button padding style={{ width: "60%" }}>
Trade
</Button>
</Link>
</Styles.TabelDataStyles>
</Styles.TableRowStyles>
);
})}
</tbody>
</Styles.Tablestyles>
</TableContainer>
);
}
I'd recommend just using a flag stored in your state to toggle how the array is displayed. This allows you to avoid storing both arrays in your state when they're essentially the same data. You'd of course need to make a click handler to change the value of the reversed flag and place it wherever you want the click to occur, but I don't see where that is in your code.
[reversed, setReversed] = useState(false);
...
// declare this variable somewhere inside your component outside the return statement.
// You have create a copy to prevent reverse() from mutating the prop in place.
const displayedCoins = reversed ? Array.from(props.coins).reverse() : props.coins;
...
{displayedCoins.filter(searchingFor(search)).map((coin) => { ... });

Categories

Resources