React: Add item in the beginning of list - javascript

I have a simple list where I'm displaying names. When I'm trying to add new name, it's added in the end of the list.
How do I manage to add new names on the top of the list ?
I tried push() and unshift() and every time I have the following error
list.map is not a function
Code :
https://codesandbox.io/s/gallant-minsky-0xgmg?file=/src/App.js
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
export default () => {
const initialList = [
{
id: "1",
name: "John"
},
{
id: "2",
name: "Doe"
},
{
id: "3",
name: "Seb"
}
];
const [list, setList] = React.useState(initialList);
const [name, setName] = React.useState("");
function handleChange(event) {
setName(event.target.value);
}
function handleAdd() {
const newList = list.concat({ name });
setList(newList);
setName("");
}
return (
<div>
<div>
<input type="text" value={name} onChange={handleChange} />
<button type="button" onClick={handleAdd}>
Add
</button>{" "}
</div>
<ul>
<div>
{list.map((item, index) => (
<li key={item.id}>
<div>{item.name}</div>
</li>
))}
</div>
</ul>
</div>
);
};

When ever you add an element to array by array.push(element) or array.unshift(element) it will always return the new length of the array.
const initialList = [
{
id: "1",
name: "John"
},
{
id: "2",
name: "Doe"
},
{
id: "3",
name: "Seb"
}
];
const [list, setList] = React.useState(initialList);
const [name, setName] = React.useState("");
function handleAdd() {
const newList = list.push({ name }); // returns new length
setList(newList);
setName("");
}
here it will set the newList value to 4 and instead of new List.
<div>
{list.map((item, index) => (
<li key={item.id}>
<div>{item.name}</div>
</li>
))}
</div>
when you try to apply map on number 4 it will return .map is not function because it works only with list of items not on numbers. same with the unshift.

You can use spread operator and have:
function handleAdd() {
const newList = [{ name }, ...list];
setList(newList);
setName("");
}

import React, { useState } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
export default () => {
const initialList = [
{
id: "1",
name: "John"
},
{
id: "2",
name: "Doe"
},
{
id: "3",
name: "Seb"
}
];
const [list, setList] = React.useState(initialList);
const [name, setName] = React.useState("");
function handleChange(event) {
setName(event.target.value);
}
function handleAdd() {
const newList = [{ name }, ...list]; // simply create a new array and spread the previous one
setList(newList);
setName("");
}
return (
<div>
<div>
<input type="text" value={name} onChange={handleChange} />
<button type="button" onClick={handleAdd}>
Add
</button>{" "}
</div>
<ul>
<div>
{list.map((item, index) => (
<li key={item.id}>
<div>{item.name}</div>
</li>
))}
</div>
</ul>
</div>
);
};

Related

How to uncheck all the checkboxes in a list of checkbox programmatically in react hooks?

I have a list of 6 items and their respective checkboxes.for instance , i have selected 4 checkboxes out of 6. Now by a click of button i have to uncheck all the 4 checkboxes that are selected to unselected.Please anyone help me with the solution here.
code:
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "./App.css";
export default function App() {
const checkList = ["Apple", "Banana", "Tea", "Coffee","orange","cookie"];
const handleCheck = (event) => {};
return (
<div className="app">
<div className="checkList">
<div className="title">Your CheckList:</div>
<div className="list-container">
{checkList.map((item, index) => (
<div key={index}>
<input value={item} type="checkbox"
onChange={handleCheck} />
<span>{item}</span>
</div>
))}
</div>
</div>
<div>
<button onclick>Reset all checkbox</button>
</div>
</div>
);
}
The array of checklist must contain the attribute that indicates if the item is checked or not, so first you need to set the list like this:
const [checkList, setCheckList] = useState([
{ item: "Apple", checked: false },
{ item: "Banana", checked: false },
{ item: "Tea", checked: false },
{ item: "Coffee", checked: false },
{ item: "orange", checked: false },
{ item: "cookie", checked: false }
]);
then on the reset function you have to set all the checked values to false:
const resetClick = () => {
for (const item of checkList) {
item.checked = false;
}
setCheckList([...checkList]);
};
and paas this function to resect button onClick event.
Below is the complete working code, hope it helps!
import { useEffect, useState } from "react";
import "./styles.css";
export default function App() {
const [checkList, setCheckList] = useState([
{ item: "Apple", checked: false },
{ item: "Banana", checked: false },
{ item: "Tea", checked: false },
{ item: "Coffee", checked: false },
{ item: "orange", checked: false },
{ item: "cookie", checked: false }
]);
const handleCheck = (event) => {
const index = checkList.findIndex((list) => list.item == event.target.name);
checkList[index].checked = event.target.checked;
setCheckList([...checkList]);
};
const resetClick = () => {
for (const item of checkList) {
item.checked = false;
}
setCheckList([...checkList]);
};
useEffect(() => {
console.log(checkList);
}, [checkList]);
return (
<div className="app">
<div className="checkList">
<div className="title">Your CheckList:</div>
<div className="list-container">
{checkList.map((item, index) => (
<div key={index}>
<input
name={item.item}
value={item.checked}
checked={item.checked}
type="checkbox"
onChange={handleCheck}
/>
<span>{item.item}</span>
</div>
))}
</div>
</div>
<div>
<button onClick={resetClick}>Reset all checkbox</button>
</div>
</div>
);
}
As you did not provide your code, I wrote something with my sense.
// example with list
const [list, setList] = React.useState([]);
// do as much as checkbox as you want, but
// with different value
<input
type={"checkbox"}
value={some_value}
checked={list.includes(some_value)}
onChange={(e) => {
e.target.checked
? setList([...list, e.target.value])
: setList([...list].filter((o) => o !== e.target.value));
}}
/>;
// on submit or click of button
const onSubmit = () => {
setList([]);
};
You can do this by using useState hook and having array of objects instead of array of strings and also directly using index of array as key is not a best practice in react.
import React, { useState } from "react";
function transformArray(arr) {
let retObj = arr.map((item, index) => ({
key: index,
item,
checked: false
}));
return retObj;
}
function App() {
let [checkList, setCheckList] = useState(
transformArray(["Apple", "Banana", "Tea", "Coffee", "orange", "cookie"])
);
const handleCheck = (event) => {
let _list = Array.from(checkList);
_list[event.target.name].checked = !_list[event.target.name].checked;
setCheckList(_list);
};
const resetCheckbox = () => {
setCheckList(
checkList.map(({ key, item }) => ({ key, item, checked: false }))
);
};
return (
<div className="app">
<div className="checkList">
<div className="title">Your CheckList:</div>
<div className="list-container">
{checkList.map((item, index) => (
<div key={item.key}>
<input
checked={item.checked}
type="checkbox"
name={index}
onChange={handleCheck}
/>
<span>{item.item}</span>
</div>
))}
</div>
</div>
<div>
<button onClick={resetCheckbox}>Reset all checkbox</button>
</div>
</div>
);
}
export default App;

Having problems with searchbar logic

so I'm a beginner and I'm trying to build a simple phonebook, where you can add persons and filter them with their name.
My problem is, that when I'm trying to add a new contact, the new contact does't show until I write something in the searchbar.
When I deleted the search component, I was able to add contacts normally.
Here is my code so far:
(App.js)
import { useState } from 'react'
import Person from './components/Person'
const App = () => {
const [persons, setPersons] = useState([
{ name: 'Arto Hellas', number: '040-123456', id: 1 },
{ name: 'Ada Lovelace', number: '39-44-5323523', id: 2 },
{ name: 'Dan Abramov', number: '12-43-234345', id: 3 },
{ name: 'Mary Poppendieck', number: '39-23-6423122', id: 4 }
])
const [newName, setNewName] = useState('')
const [newNumber, setNewNumber] = useState('')
const[newSearch, setNewSearch] = useState('')
const[personsFilter, setPersonsFilter] = useState(persons)
const addContact = (event) => {
event.preventDefault()
const nameObject = {
name: newName,
number: newNumber,
id: persons.length+1,
}
const currentPerson = persons.filter((person) => person.name === newName);
if (currentPerson.length === 1) {
alert(`${newName} is already added to phonebook`)
} else {
setPersons(persons.concat(nameObject))
setNewName('')
setNewNumber('')
}
}
const handleNameChange = (event) => {
setNewName(event.target.value)
}
const handleNumberChange = (event) => {
setNewNumber(event.target.value)
}
const filterPersons = (event) => {
const searchName = event.target.value.toLowerCase()
setNewSearch(searchName)
const newPersons = persons.filter (
(person) =>
person.name.toLowerCase().search(searchName) !== -1
)
setPersonsFilter(newPersons)
}
return (
<div>
<h2>Phonebook</h2>
<div>
filter:
<input value={newSearch}
onChange={filterPersons}
/>
</div>
<form onSubmit={addContact}>
<div>
name: <input
value={newName}
onChange={handleNameChange}
/>
</div>
<div>
number: <input
value={newNumber}
onChange={handleNumberChange}/>
</div>
<div>
<button type="submit">add</button>
</div>
</form>
<h2>Numbers</h2>
<ul>
<Person persons={personsFilter} />
</ul>
</div>
)
}
export default App
(Persons.js)
import React from "react";
const Person = ({ persons }) => {
return persons.map((person) =>
<li key={person.id}>{person.name}: <span>{person.number}</span></li>
)
}
export default Person
and index.js
import React from 'react';
import ReactDOM from 'react-dom'
import App from './App'
ReactDOM.render(<App />, document.getElementById('root'))
I hope I explained my error clearly lol
Thanks in advance
Have great day <3
The problem with your code is when you set a new person it only updates the persons state not the personsFilter state. The personsFilter state is the one being used to show data to the DOM. The reason it works when you filter is because your function filterPersons() takes a copy of the persons state.
Instead of focusing on two states why not just use a single searchTerm state then just filter that when you map?
This is how I would do it:
App.js
import { useState } from 'react'
import Person from './components/Person'
const App = () => {
const [persons, setPersons] = useState([
{ name: 'Arto Hellas', number: '040-123456', id: 1 },
{ name: 'Ada Lovelace', number: '39-44-5323523', id: 2 },
{ name: 'Dan Abramov', number: '12-43-234345', id: 3 },
{ name: 'Mary Poppendieck', number: '39-23-6423122', id: 4 }
])
const [newName, setNewName] = useState('')
const [newNumber, setNewNumber] = useState('')
const[searchTerm, setSearchTerm] = useState('')
const addContact = (event) => {
event.preventDefault()
const nameObject = {
name: newName,
number: newNumber,
id: persons.length+1,
}
const currentPerson = persons.filter((person) => person.name === newName);
if (currentPerson.length === 1) {
alert(`${newName} is already added to phonebook`)
} else {
setPersons(persons.concat(nameObject))
setNewName('')
setNewNumber('')
}
}
const handleNameChange = (event) => {
setNewName(event.target.value)
}
const handleNumberChange = (event) => {
setNewNumber(event.target.value)
}
const filterPersons = (event) => {
setSearchTerm(event.target.value.toLowerCase())
}
return (
<div>
<h2>Phonebook</h2>
<div>
filter:
<input value={searchTerm}
onChange={filterPersons}
/>
</div>
<form onSubmit={addContact}>
<div>
name: <input
value={newName}
onChange={handleNameChange}
/>
</div>
<div>
number: <input
value={newNumber}
onChange={handleNumberChange}/>
</div>
<div>
<button type="submit">add</button>
</div>
</form>
<h2>Numbers</h2>
<ul>
<Person persons={persons} searchTerm={searchTerm} />
</ul>
</div>
)
}
export default App
Person.js
const Person = ({ persons, searchTerm }) => {
return persons
.filter((person) => person.name.toLowerCase().includes(searchTerm))
.map((person) => (
<li key={person.id}>
{person.name}: <span>{person.number}</span>
</li>
));
};
export default Person;
https://codesandbox.io/s/silly-architecture-iu8qkz?file=/src/App.js

How to use React Select if the object properties's are not value and label?

import { Fragment, useState } from "react";
import Select from 'react-select';
let items = [
{
item: 1,
name: "tv"
},
{
item: 2,
name: "PC"
}
]
const Home = () => {
const [selectedValue, setSelectedValue] = useState(6)
const handleChange = obj => {
setSelectedValue(obj.item)
}
return (
<Fragment>
<div>Home page</div>
<p>Test React Select...</p>
<Select
value={items.find(x => x.item === selectedValue)}
options={items}
onChange={handleChange}
/>
<p>selected Value:...</p>
{selectedValue}
</Fragment>
)
}
export default Home;
you can pass the mapped array to "options" property:
options={items.map(({item, name}) => ({value: name, label: item}))}

Add or remove elements based on checkbox status using react js

I have a simple section in which contains products with multiple checkboxes and default prices,
I want when the checkbox is true to show its price and remove all the rest pricess obvious those with the false state. if all checkboxes are false then show all the prices
Live demo : live demo
Here is what I have so far //toppings.js
export const toppings = [
{
name: "Capsicum",
price: 1.2
},
{
name: "Paneer",
price: 2.0
},
{
name: "Red Paprika",
price: 2.5
},
{
name: "Onions",
price: 3.0
},
{
name: "Extra Cheese",
price: 3.5
},
{
name: "Baby Corns",
price: 3.0
},
{
name: "Mushroom",
price: 2.0
}
];
Here is my solution
import { toppings } from "./utils/toppings";
export default function App() {
const [checkedState, setCheckedState] = useState(
new Array(toppings.length).fill(false)
);
const handleOnChange = (position) => {
const updatedCheckedState = checkedState.map((item, index) =>
index === position ? !item : item
);
setCheckedState(updatedCheckedState);
const elements = updatedCheckedState.filter((currentState, index) => {
if (currentState === false) {
delete toppings[index].price;
} else if (currentState === false) {
toppings[index] = toppings[index].price;
console.log("current state", currentState);
}
return 0;
});
console.log(elements);
};
return (
<div className="App">
<ul className="toppings-list">
{toppings.map(({ name, price }, index) => {
return (
<li key={index}>
<div className="toppings-list-item">
<div className="left-section">
<input
type="checkbox"
id={`custom-checkbox-${index}`}
name={name}
value={name}
checked={checkedState[index]}
onChange={() => handleOnChange(index)}
/>
<label htmlFor={`custom-checkbox-${index}`}>{name}</label>
</div>
</div>
</li>
);
})}
</ul>
<ul className="toppings-list">
{toppings.map(({ name, price }, index) => {
return <li key={index}> {price} </li>;
})}
</ul>
</div>
);
}
Unfortunately this is not working as expected, can someone tell me what am doing wrong here
Here is a simple working example.
storing all selected item indexes in a state array
Live example -
https://codesandbox.io/s/condescending-sara-t5ws3?file=/src/App.js
import { useState } from "react";
import { toppings } from "./utils/toppings";
import "./styles.css";
export default function App() {
const [checked, setChecked] = useState([]);
const handleChecked = (e, index) => {
let prev = checked;
let itemIndex = prev.indexOf(index);
if (itemIndex !== -1) {
prev.splice(itemIndex, 1);
} else {
prev.push(index);
}
setChecked([...prev]);
};
return (
<div className="App">
<ul className="toppings-list">
{toppings.map(({ name, price }, index) => {
return (
<>
<li key={index}>
<div className="toppings-list-item">
<span className="left-section">
<input
type="checkbox"
id={`custom-checkbox-${index}`}
// name={name}
// value={name}
checked={checked.includes(index)}
onChange={(e) => handleChecked(e, index)}
/>
<label htmlFor={`custom-checkbox-${index}`}>{name}</label>
</span>
{(!checked.length || checked.includes(index)) && (
<span>{price}</span>
)}
</div>
</li>
</>
);
})}
</ul>
</div>
);
}

How to show a button only in the last item from a list?

I'm still a beginner in ReactJS.
I have a list where I can go adding new lines. To add a line I use the plus button, and to remove the line I use the minus button.
I would like to know, how can I show the plus button only on the last item in my list? So that the plus button doesn't repeat itself unnecessarily.
Could you tell me how can I remove that unnecessary plus buttons? Thank you in advance.
Here's my code I put into codesandbox.
import React from "react";
import "./styles.css";
import List from "./List/List";
const App = () => {
const [data, setData] = React.useState([
[
{
label: "Name",
value: "",
name: "00"
},
{
label: "Last Name",
value: "",
name: "01"
}
]
]);
const handleOnChange = (e, row, col) => {
const newData = data.map((d, i) => {
if (i === row) {
d[col].value = e.target.value;
}
return d;
});
setData(newData);
};
const addRow = () => {
console.log(data);
setData([
...data,
[
{
label: "Name",
value: "",
name: `${data.length}0`
},
{
label: "Last Name",
value: "",
name: `${data.length}1`
}
]
]);
};
const removeRow = (index) => {
const _data = [...data];
_data.splice(index, 1);
setData(_data);
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<List
data={data}
addRow={addRow}
removeRow={removeRow}
handleOnChange={handleOnChange}
/>
</div>
);
};
export default App;
import React from "react";
import AddCircleIcon from "#material-ui/icons/AddCircle";
import RemoveCircleIcon from "#material-ui/icons/RemoveCircle";
import TextField from "#material-ui/core/TextField";
import "./styles.scss";
const List = ({ data, handleOnChange, addRow, removeRow }) => {
return (
<div className="container">
{data.map((items, i1) => (
<div key={i1} className="content">
<div className="content-row">
{items.map((item, i2) => (
<TextField
key={i2}
label={item.label}
value={item.value}
onChange={(e) => handleOnChange(e, i1, i2)}
variant="outlined"
name={item.name}
/>
))}
</div>
<div>
<AddCircleIcon onClick={addRow} />
{data.length > 1 && (
<RemoveCircleIcon onClick={() => removeRow(i1)} />
)}
</div>
</div>
))}
</div>
);
};
export default List;
You will need to add a condition when rendering your plus button:
So in your List Component:
Replace this:
<AddCircleIcon onClick={addRow} />
TO
{i1 === data.length - 1 && <AddCircleIcon onClick={addRow} />}
Working example

Categories