removing an item from an array in ReactJS - javascript

I am learning React and I can't seem to figure out how to remove an item from an array. I have tried couple of ways but it either removes the entire array or more than one item in the array so I can't find any working solution. Here is my code:
App.js:
import React, { useState } from 'react';
import Education from './Education';
export default function App() {
const [educationArray, setEducationArray] = useState([]);
const handleDeleteEduItem =(id)=>{
const eduArrayToSplice = [...educationArray]
const newEduArray = eduArrayToSplice.splice(id, 1)
setEducationArray(newEduArray)
console.log(id)
}
return (
<div className="App">
<div className="edit-mode">
<h4>Education</h4>
<button onClick={()=>{setEducationArray([...educationArray, <Education id={educationArray.length} handleDeleteButton={handleDeleteEduItem}/>])}}>add new</button>
{educationArray.map((eduItem, i)=><div className="eduItem" key={i}>{eduItem}</div>)}
</div>
</div>
);
}
And the functional component:
import React, { useState} from 'react';
function Education(props)
{
const [schoolName, setSchoolName] = useState('');
const [major, setMajor] = useState('');
const [studyFrom, setStudyFrom] = useState('')
const [studyTo, setStudyTo] = useState('');
const [degree, setDegree] = useState('');
const [displayEducationSection, setEducationSection] = useState(false)
const changeSchoolName = (e) => {
setSchoolName(e.target.value);
};
const changeMajor = (e) => {
setMajor(e.target.value);
};
const changeStudyFrom =(e)=> {
setStudyFrom(e.target.value);
};
const changeStudyTo =(e)=> {
setStudyTo(e.target.value)
};
const changeDegree =(e) => {
setDegree(e.target.value);
};
const flipEducationSection =()=> {
setEducationSection(!displayEducationSection)
};
return(
<div className="education-section">
{displayEducationSection ?
<div>
<p>School Name: {schoolName}</p>
<p>Major: {major}</p>
<p>from: {studyFrom}</p>
<p>to: {studyTo}</p>
<p>Degree: {degree}</p>
</div>
:
<form onSubmit={(e)=>e.preventDefault()} className="education-form">
<label>
School Name:<input value={schoolName} onChange={changeSchoolName} />
</label>
<label>
Major:<input value={major} onChange={changeMajor} />
</label>
<label>
From:<input value={studyFrom} onChange={changeStudyFrom} />
</label>
<label>
To:<input value={studyTo} onChange={changeStudyTo} />
</label>
<label>
Degree:<input value={degree} onChange={changeDegree} />
</label>
</form>}
<button onClick={flipEducationSection}>{displayEducationSection ? 'edit' : 'save'}</button>
<button onClick={()=>props.handleDeleteButton(props.id)}>delete</button>
</div>
)
}
export default Education;
I've also used the following function to try to remove an item from the array, but it doesn't remove the clicked item but removes all items that come after it:
const handleDeleteEduItem =(id)=>{
const newEduArray = educationArray.filter((item)=>educationArray[item] !== id)
setEducationArray(newEduArray)
console.log(educationArray)
}

I think you don't want to filter directly the state. What you could do instead is:
setEducationArray((cv) => cv.filter((e) => e.id !== id ))
This way you get to access the current value in your educationArray state (cv), and filter that to get all elements where the e.id is not equal to the id you have given to your id.
Edit:
To be fair, I'm not sure how your array eventually looks like. But if it was an array of objects, with each object having its own id. Then I would suggest the thing I wrote above.

you can not directly update the state with using same memory location.
you have to create new memory to updated array and then update the component state. you will see quickly changes on UI.
else this function will help you to remove single item from array.
you have to
ensure that your id should be unique for each item.
const handleDeleteEduItem =(id)=>{
const eduArrayToSplice = [...educationArray.filter(item)=>item.id!==id)]
setEducationArray(newEduArray)
console.log(id)
}

Related

When I am using onChage here, it takes only second change. The first change I've tried in the input is not taking

Why the input only taking inputs from second input only?
import React, { useState } from "react";
import Item from "./Components/Item";
import "./ToDo.css";
function ToDo() {
let toDoIs = document.getElementById("toDoInput");
const [ToDo, setToDoIs] = useState("d");
const [ToDoArray, setToDoArray] = useState([]);
return (
<div>
<h1>ToDo</h1>
<input
id="toDoInput"
onChange={() => {
setToDoIs(toDoIs.value);
}}
type="text"
/>
<button
onClick={() => {
setToDoArray([...ToDoArray, { text: ToDo }]);
toDoIs.value = "";
}}
>
Add
</button>
<Item push={ToDoArray} />
</div>
);
}
export default ToDo;
Why the second input only works, which means whenever I use submit the value from second input only stored and displayed. I don't know why this happens.
There's a few problems here...
Don't use DOM methods in React. Use state to drive the way your component renders
Your text input should be a controlled component
When updating state based on the current value, make sure you use functional updates
import { useState } from "react";
import Item from "./Components/Item";
import "./ToDo.css";
function ToDo() {
// naming conventions for state typically use camel-case, not Pascal
const [toDo, setToDo] = useState("d");
const [toDoArray, setToDoArray] = useState([]);
const handleClick = () => {
// use functional update
setToDoArray((prev) => [...prev, { text: toDo }]);
// clear the `toDo` state via its setter
setToDo("");
};
return (
<div>
<h1>ToDo</h1>
{/* this is a controlled component */}
<input value={toDo} onChange={(e) => setToDo(e.target.value)} />
<button type="button" onClick={handleClick}>
Add
</button>
<Item push={toDoArray} />
</div>
);
}
export default ToDo;

How can I check the radio button with a single click? and how to select one by default?

I am trying to make a simple react app that displays radio input for each 'hero' from a list of heroes and if the user checks, the hero's name will be displayed as the favorite hero. But the problem is on my local machine to check a hero I need to double click that radio input. How can I check a hero with a single click?
Code of the app.js file is given below:
import React, { useState } from "react";
import "./App.css";
function App() {
const [heros, setHeros] = useState([
"Superman",
"Batman",
"Antman",
"Robocop",
]);
const [selected, setSelected] = useState(null);
const handleChange = (e) => {
e.preventDefault();
setSelected(e.target.value);
console.log(e.target.checked);
};
return (
<div>
<h1>Select Your Favorite Hero</h1>
<form onChange={handleChange}>
{heros.map((hero, index) => (
<div key={index}>
<input
type="radio"
name="hero"
id={hero}
value={hero}
/>
<label htmlFor="{hero}">{hero}</label>
<br />
</div>
))}
</form>
<div>
<p>Your super hero is: {selected}</p>
</div>
</div>
);
}
export default App;
Remove e.preventDefault() inside handleChange function. Function will be like this one.
const handleChange = (e) => {
setSelected(e.target.value);
console.log(e.target.checked);
};
First, select the button with #btn id, output element with the #output id, and all the radio buttons with the name heros
const btn = document.querySelector('#btn');
const output = document.querySelector('#output');
const radioButtons = document.querySelectorAll('input[name="heros"]');

Creating tags and passing props up components to filter in React

I am putting together an app using React that displays fetched data in a card component and filters by name through in input form. The displayed data also has an input field to create tags in each card.
What I would like to do is then be able to create an additional filter to filter the cards by the tags in addition to the name.
I am able to create the student cards, filter by name, and add the tags to each student card. What I would like to do is add the tags to the student hook in App.js. I have tried passing back the tags via Tag props from the StudentCard.js but I could not figure out a way to pass the student.id with the tags to update and filter the student hook in App.js correctly. I'm relatively new to React so I'm thinking I may be missing something obvious but it's also possible what I'm trying to do cannot be accomplished in this way. Any help would be greatly appreciated.
For the sake of simplicity I have cut the components down and removed the fetch API to make it easier to review.
The components are below:
Example of App.js:
import './App.css';
import StudentCard from "./StudentCard";
function App() {
const [student, setStudent] = useState([])
const [studentFilter, setStudentFilter] = useState('')
useEffect(() => {
const loadStudent = async () => {
const student = await fetch(`https://someAPI.com`)
.then(res => res.json())
.then(data => data)
setStudent(student)
}
loadStudent()
}, [])
// Example of student = "students": [
// {
// "city": "SF",
// "email": "tom#t.com",
// "firstName": "Tom",
// "id": "1",
// "lastName": "Holmes",
// },
let studentsArray = student.students
let students = studentsArray && studentsArray.filter(f =>
(f.firstName + ' ' + f.lastName).toLowerCase().includes(studentFilter) || studentFilter === '')
.map((students, id) =>
<StudentCard key={id} students={students}/>
)
return (
<div className="App">
<p>
<input id="filter"
name="filter"
type="text"
value={studentFilter}
placeholder="Search by name"
onChange={event => setStudentFilter(event.target.value)}
/>
</p>
//I'm having an issue figuring out how to make this filter work
<p>
<input id="tag-input"
type="text"
name="tag-filter"
value=""
placeholder="Search by tag"
/>
</p>
{students}
</div>
);
}
export default App;
Example of Student Card:
import React, { useState } from 'react';
import TagsInput from './TagsInput'
const StudentCard = ({students}) => {
const selectedTags = tags => tags;
return (
<div className="student-info-container">
<div className="student-info">
<div className="student-name">
{students.firstName} {students.lastName}
</div>
<div className="indent-info">
<div>
Email: {students.email}
</div>
<div>
<TagsInput selectedTags={selectedTags} studentId={students.id}/>
</div>
</div>
</div>
</div>
);
}
export default StudentCard;
Example of TagInput.js
import React, {useState} from "react";
const TagsInput = (props) => {
const [tags, setTags] = useState([]);
const addTags = event => {
if (event.key === "Enter" && event.target.value !== "") {
setTags([...tags, event.target.value]);
props.selectedTags([...tags, event.target.value, props.studentId]);
event.target.value = "";
}
};
console.log(props)
return (
<div className="tags-input">
<div className="tag-container">
{tags.map((tag, index) => (
<span className="tag" key={index}>{tag}</span>
))}
</div>
<input
id="tag-filter"
type="text"
onKeyUp={event => addTags(event)}
placeholder="Add a tag"
/>
</div>
);
};
export default TagsInput;
```

Creating div in function (React)

I have an exercise where I have to make an input and a button. When I click the button, there has to be created a div/span below, which prints the text which is in input. If I change the text in input, it has to be refreshed in that div/span only when I click the button again. I tried to do it with makeDiv function, but it doesn't do anything. I made console.log(event.target.value) and it handles the text which is in input, but nothing happens then.
My code:
import {useState} from "react"
function About() {
const [initialValue,setInitialValue] = useState('')
const handleValueChange = (event) => {
console.log(event.target.value)
setInitialValue(event.target.value)
}
const makeDiv = () => {
return (<div>Value: {initialValue}</div>)
}
return(
<div>
<button onClick={makeDiv}>click me</button>
<div><input type="text" onChange={handleValueChange} /></div>
</div>
)
}
export default About
edit:
What if I wanted to make an exercise very similar to that, but now, I have to add <li>text in input</li> to <ul> each time I click the button. So when I click the button, I add one li to the list, I tried like this, but it doesn't compile:
import {useState} from "react"
function About() {
const [initialValueLastExercise, setInitialValueLastExercise] = useState([])
const [ValueLE, setValueLE] = useState([])
const handleValueChangeLE = (event) => {
console.log(event.target.value)
setInitialValueLastExercise([...initialValueLastExercise, event.target.value])
}
const showListWithText = () => {
setShouldDisplayText(true)
setValueLE(initialValueLastExercise)
}
return(
<div>
<button onClick={showListWithText}>click me to refresh the list</button>
<div><input type="text" onChange={handleValueChangeLE} /></div>
{shouldDisplayText && <div><ul>
{
for (let i =0; i<initialValueLastExercise.length; i++) {
<li>{initialValueLastExercise[i]}</li>
}
}</div></ul>}
</div>
)
}
export default About
This will refresh the value of the div on button click only as you have mentioned in the question.
import {useState} from "react"
function App() {
const [initialValue,setInitialValue] = useState('')
const [displayText, setDisplayText] = useState(false)
const [Value,setValue] = useState('')
const handleValueChange = (event) => {
setInitialValue(event.target.value)
}
const showText = () => {setDisplayText(true)
setValue(initialValue)};
return(
<div>
<button onClick={showText}>click me</button>
<div><input type="text" onChange={handleValueChange} /></div>
{displayText && <div>Value: {Value}</div>}
</div>
)
}
export default App
Solution for the Edited Question.
import {useState} from "react"
function App() {
const [initialValue,setInitialValue] = useState('')
const [displayText, setDisplayText] = useState(false)
const [Value,setValue] = useState([])
const handleValueChange = (event) => {
setInitialValue(event.target.value)
}
const showText = () => {setDisplayText(true)
setValue([...Value,initialValue])};
return(
<div>
<button onClick={showText}>click me</button>
<div><input type="text" onChange={handleValueChange} /></div>
<ul>{displayText && Value.length > 0 &&
Value.map((i) => {
return <li>Value: {i}</li>
})}</ul>
</div>
)
}
export default App
One way to do it is to create another state variable which indicates whether the div you're trying to make should be displayed and then render it conditionally. Something like
import {useState} from "react"
function About() {
const [initialValue,setInitialValue] = useState('')
const [shouldDisplayText, setShouldDisplayText] = useState(false)
const handleValueChange = (event) => {
console.log(event.target.value)
setInitialValue(event.target.value)
}
const showDivWithText = () => setShouldDisplayText(true);
return(
<div>
<button onClick={showDivWithText}>click me</button>
<div><input type="text" onChange={handleValueChange} /></div>
{shouldDisplayText && <div>Value: {initialValue}</div>}
</div>
)
}
export default About
Your approach is fundamentally wrong.
You should:
Store all the data about the component in the state
Render the output based on the state
So:
You need two state variables:
currentInputValue (because you need to store the value to display and edit in input)
selectedValue (because you need to store the value to be displayed in the div)
When onChange fires, update currentInputValue with the value of the input.
When onClick fires, update selectedValue with the current value of currentInputValue
When you return your data, include something like:
{selectedValue && <div>{selectedValue}</div>}
… to output a div containing the selected value only if there is a truthy value (the default empty string isn't truthy so the div won't be output then)
1st possibility - close to your code source
Don't forget to bind initialValue to the input and to add makeDiv content to the JSX :
return (
<div>
<button onClick={makeDiv}>click me</button>
<input type="text" onChange={handleValueChange} value={initialValue} />
{makeDiv}
</div>
)
2nd possibility - with another approach
return (
<div>
<button onClick={makeDiv}>click me</button>
<input type="text" onChange={handleValueChange} value={initialValue} />
{initialValue && <div>{initialValue}</div>}
</div>
)

Display data from input fields in react

Good afternoon everyone.
I have a dropdown with two input fields inside. Name and Price.
I would like to display the name and price after I click Set button that it appears in the same dropdown but on top of input fields.
Here is how it looks in my app currently, I enter name and price by myself.
As you can see in a first field there is a name and in the second there is a number and I wan't to store it under Price Alert History after clicking Set button.
Here is how I wish it will look. It's just an example which was made in photoshop. The main thing that I want to see name and price on top of input field.
CODE HERE
import React from "react";
import { Button} from "react-bootstrap";
const symbols = [
"ADABTC",
"AIONBTC",
"ALGOBTC",
"ARDRBTC",
"KAVABTC",
"ETHBTC",
"ETCBTC"
];
function PriceTriggerField() {
const [searchTerm, setSearchTerm] = React.useState("");
const [searchSymbol, setSearchSymbol] = React.useState([]);
const handleChangeTerm = event => {
setSearchTerm(event.target.value);
};
const handleChangeSymbol = event => {
setSearchSymbol(event.target.value);
};
React.useEffect(() => {
const results = symbols.filter(symbols =>
symbols.toUpperCase().includes(searchTerm)
);
setSearchSymbol(results);
}, [searchTerm]);
return (
<div className="App">
<h6>Price Alert History</h6>
<input
type="text"
placeholder="Symbol"
value={searchTerm}
onChange={handleChangeTerm}
/>
<input
type="number"
placeholder="Price"
/>
{
searchTerm.length > 0 && searchSymbol.map(item => <li onClick={(() => setSearchTerm(item) )}>{item}</li>)
}
<Button variant="secondary">Set</Button>
</div>
);
}
export default PriceTriggerField;
this is just a simple example with only one variable, but of course, you can do that for as many variables as you wish.
import React, { useState } from "react";
export default function App() {
const [name, setName] = useState(null);
let tmpName;
const onChange = e => {
tmpName = e.target.value;
}
return (
<div className="App">
<input onChange={onChange} />
<button onClick={() => setName(tmpName)}>set</button>
name: {name}
</div>
);
}

Categories