Remove item from array in React - reactjs

I have problem with removeItem function (it should remove current <li> that button is nested in, and item from array on this.state.list), no code currently because I try so much things of that and nothing working so I end up on console.logs watch what happened so I deleted it
import React, { Component } from 'react';
import './Todo.css';
class Todo extends Component {
constructor(props) {
super(props);
this.state = {
list: [],
text: ''
}
this.textChange = this.textChange.bind(this);
this.addToList = this.addToList.bind(this);
this.removeItem = this.removeItem.bind(this);
}
textChange(e) {
this.setState({
text: e.target.value
})
}
addToList() {
this.setState(prevState => ({
list: prevState.list.concat(this.state.text),
text: ''
}))
}
removeItem(e) { ?
? ? ? ? ? ? ?
}
render() {
return(
<div>
<h1>My Todo List</h1>
<h3>Add item</h3>
<input value={this.state.text} onChange={e => this.textChange(e)}/>
<button onClick={this.addToList}>+</button>
<ul>{this.state.list.map((x,y) => {
return <li key={y}>{x}
<button onClick={this.removeItem}>-</button>
</li>})}
</ul>
</div>
)
}
}
export default Todo;

in my solution
eg:
const remove = (i) => {
const arr = data.filter((item) => item.name !== i);
setData(arr);
};
I filtered the items that are not removed and set again the state

Removing item from array by index:
const newList = this.state.list.splice(index, 1);
Removing item from array by value:
const newList = this.state.list.splice(this.state.list.indexOf(value), 1);

You can filter your list by the issue you want,
and it will be auto removed,
for example, if you want to remove all items = 3 :
list: prevState.list.filter(x=> x != 3);
Good luck!

removeItem(item) {
const item = getItem(this.state.list, item.id) // Method to get item in list through comparison (IE: find some item with item.id), it has to return ITEM and INDEX in array
const newlist = [].concat(list) // Clone array with concat or slice(0)
newlist.splice(item.index, 1);
this.setState({list: newlist});
}

I think you should pass the index of the item to your removeItem function. Like so:
removeItem(index) {
const list = this.state.list;
list.splice(index, 1);
this.setState({ list });
}
render() {
return(
<div>
<h1>My Todo List</h1>
<h3>Add item</h3>
<input value={this.state.text} onChange={e => this.textChange(e)}/>
<button onClick={this.addToList}>+</button>
<ul>{
this.state.list.map((text, i) => {
return (
<li key={i}>
{text}
<button onClick={() => this.removeItem(i) }>-</button>
</li>
);
})}
</ul>
</div>
)
}

I would pass the index of the item in the list on click then splice the array:
<ul>
{
this.state.list.map((x,y) => {
return (
<li key={y}>
{x}
<button onClick={() => this.removeItem(y)}>-</button>
</li>
);
})
}
</ul>
Then in removeItem:
removeItem(index) {
const list = this.state.list;
list.splice(index, 1);
this.setState({ list });
}

import React, { Component } from 'react';
import './Todo.css';
class Todo extends Component {
constructor(props) {
super(props);
this.state = {
list: [],
text: ''
}
this.textChange = this.textChange.bind(this);
this.addToList = this.addToList.bind(this);
}
textChange(e) {
this.setState({
text: e.target.value
})
}
addToList() {
this.setState(prevState => ({
list: prevState.list.concat(this.state.text),
text: ''
}))
}
removeItem(index) {
let newList = this.state.list.splice(index,1);
this.setState({list:newList})
}
render() {
return(
<div>
<h1>My Todo List</h1>
<h3>Add item</h3>
<input value={this.state.text} onChange={e => this.textChange(e)}/>
<button onClick={this.addToList}>+</button>
<ul>{this.state.list.map((x,y) => {
return <li key={y}>{x}
<button onClick={this.removeItem.bind(this,y)}>-</button>
</li>})}
</ul>
</div>
)
}
}
export default Todo;

_deleteTodo(index) {
console.log("delete " + index);
this.state.todos.splice(index, 1);
this.setState({
todos: this.state.todos.filter(i => i !== index)
});
}
I had a problem with splice and i honestly don know why. However this method work for me and you can try it! Ps. If anybody know why splice is not working with state and index please let me know i am curious!

Related

TypeError: list.push is not a function

I was trying ReactJS and got an error. I am trying to push a string into an array of list and when the user clicks the button the following error comes up:
TypeError: list.push is not a function
I also used the concat function but this error comes up there as well.
Here's the function this occurs inside of:
adddata(todovalue){
if(todovalue !== ""){
const newitem = {
id : Date.now(),
value: todovalue,
isDone: false
};
const list = {...this.state.list};
list.push(newitem); //This is where the error occurs
this.setState({
list,
newitem : ""
});
}
}
For context, here's my entire app:
import React from 'react';
import logo from './logo.png';
import './App.css';
class App extends React.Component{
constructor(props){
super(props);
this.state={
newitem : "",
list : []
};
}
adddata(todovalue){
if(todovalue !== ""){
const newitem = {
id : Date.now(),
value: todovalue,
isDone: false
};
const list = {...this.state.list};
list.push(newitem);
this.setState({
list,
newitem : ""
});
}
}
deleteitem(id){
const list = [...this.state.list];
const udate = list.filter(item => item.id !== id);
this.setState({
list : udate
});
}
update(input){
this.setState({
newitem:input
});
}
render(){
return(
<div>
<img src={logo} width="100px " className="logo"/>
<div className="add">
<p>Enter your name</p><br></br>
<input type="text" className="input" value={this.state.newitem} onChange={e => this.update(e.target.value)}/>
<button className="btn" onClick={() => this.adddata(this.state.newitem)}>Add</button>
</div>
<div className="list">
<ul>
{this.state.list.map(item => {
return(
<li key={item.id}>
{item.value}
</li>
);
})}
<li>PLay Csgo</li><button className="del">Delete</button>
</ul>
</div>
</div>
);
}
}
export default App;
You are trying to apply spread on a object, spread the array instead and then apply the push operation on the array
Try this instead
adddata(todovalue) {
if (todovalue !== "") {
const newitem = {
id: Date.now(),
value: todovalue,
isDone: false
};
const list = [...this.state.list];
list.push(newitem);
this.setState({
list,
newitem: ""
});
}
}

onClick event handling to call function on that particular button

import React from 'react';
import './MenuCard.css';
class MenuCard extends React.Component {
constructor(props) {
super(props);
this.state = {
showButton: false,
hideButton: true,
aValue: 1,
breads: [],
category: [],
ids: 0
};
this.onShowButton = this.onShowButton.bind(this);
}
onShowButton = (id) => {
this.setState({
showButton: !this.state.showButton,
hideButton: !this.state.hideButton
}));
}
onValueIncrease = () => {
this.setState({aValue: this.state.aValue + 1});
}
onValueDecrease = () => {
this.setState({aValue: this.state.aValue - 1});
}
render() {
return (
<div>
{this.state.category.map(types => {
return (<div>
<div className="menu-head">{types}</div>
< div className="container-menu">
{this.state.breads.map((d, id)=> {
if (d.category === types) {
return (
<div className="content">
<div className="items"> {d.item_name}</div>
<div className="prices"> {d.price} Rs.</div>
<button id ={id} onClick={() => this.onShowButton(d.id)}
hidden={this.state.showButton}
className="add-menu-btn"> add
</button>
<span key={d.id} hidden={this.state.hideButton}>
<button id={d.id} className="grp-btn-minus"
onClick={this.state.aValue <= 1 ? () => this.onShowButton(d.id) : () => this.onValueDecrease(d.id)}>-
</button>
<input className="grp-btn-text" type="text"
value={this.state.aValue} readOnly/>
<button id={d.id} className="grp-btn-plus"
onClick={() => this.onValueIncrease(d.id)}>+
</button>
</span>
</div>
)
}
})}
</div>
</div>)
})}
</div>
)
}
There are multiple buttons according to items 1.
And here the problem when I click on single button all get updated I need only a single button to click with a single update 2
You need to keep the values in an array in the state, i.e:
values: [
{ id: 1, value: 20},
{ id: 2, value: 1}
]
If you then need to set the state, could look like this:
const values = Object.assign({}, this.state.values, { [id]: value })
this.setState({ values })
To get the value from state:
const value = this.state.values[id]

react axios getting values one by one

I am working on a quiz in react. I want to show one question and their choices at the same time at the page.
Such as :
"question": "When the C programming language has first appeared?",
a.)1970
b.)1971
c.)1972
d.)1973
This is what I have done so far:
import React from 'react'
import axios from 'axios'
class QuizApp extends React.Component {
constructor(props) {
super(props);
this.state = {entered: false, correct: 0, wrong: 0}
}
state = {
questions: [],
choic: []
};
componentDidMount() {
axios.get("http://private-anon-c06008d89c-quizmasters.apiary-mock.com/questions").then((response) => {
const questions = response.data;
this.setState({questions});
const choic = response.data;
this.setState({choic});
console.log(response);
})
}
nickChange = (event) => {
this.setState({username: event.target.value});
};
cleanPage = () => {
this.setState({
entered: true
})
};
render() {
if (!this.state.entered) {
return (
<div>
<form target="_self" id="firstPage">
<input type="text" value={this.nick} onChange={this.nickChange}/>
<input type="submit" value="Start" name="cleanPage" onClick={this.cleanPage}/>
</form>
</div>
)
}
else {
return (
<div>
<ul>
{this.state.questions.map(que => <li>{que.question} </li>)}
{this.state.choic.map(cho => <li>{cho.choices.choice} </li>)}
</ul>
</div>
)
}
}
}
simplify set state in response
const { questions, choices } = response.data;
this.setState({questions, choices});
In render do like this (Assuming each question has corresponding array of choices in that index):
{this.state.questions.map((que, index) => {
<React.Fragment>
<li>{que.question} </li>
<ul>
{choices[index].map(choice => <li>{choice}</li>)
</ul>
<React.Fragment
})}
style the elements as needed

Referencing a react component from its class method

I have a dynamic todo list I would like to add a "highlight" feature to. Each list item renders with markup for the highlight that should show only for the list item clicked.
export class Todo extends Component {
constructor(props) {
super(props);
this.state = {input: '', todos: this.getOldTodo()};
this.selectItem = this.selectItem.bind(this);
}
//shortened
selectItem(i) {
this.setState({selected: i});
if (this.state.selected == i) {
// --- this is the code that needs to change the right list items child's class
???.props.childen[2].className = "active";
// ---
console.log("true")
}
console.log(i);
}
render() {
//markup also shortened
this.state.todos.map((todos, i) => {
return (
//What do I pass to the method here?
<li key={todos.key} className="todo-li-item" onClick={this.selectItem.bind(this, i)}>
<span className="todo-item">{todos.text}</span>
<span onClick={this.deleteItem.bind(this, i)} className="delet-todo">✕</span>
// --- This is the child that needs its class changed when it's parent is clicked
<div id="todo-select" className={"hidden"}>
<span id="todo-select-top"></span>
<span id="todo-select-left"></span>
</div>
</li>
);
})
</ul>
</div>
);
}
}
This is painfully simple and yet so un-obvious as to what I use to do this in react, but hey I'm still learning. Thanks for your time.
You've been quite close. Here's my implementation.
Key takeaway: Don't mutate the state object.
selectItem(idx) {
this.setState(state => {
const todos = [
state.todos.slice(0, idx),
{ ...state.todos[idx], selected: ! state.todos[idx].selected },
state.todos.slice(idx + 1, state.todos.length),
]
return {
...state,
todos,
}
})
}
deleteItem(idx) {
this.setState(state => {
const todos = [...state.todos]
todos.splice(idx, 1)
return {
...state,
todos,
}
})
}
render() {
return (
<div>
<ul>
{this.state.todos.map((todo, idx) => (
<li
key={todo.key}
className={'todo-li-item'}
onClick={this.selectItem.bind(this, idx)}
>
<span className="todo-item">{todo.text}</span>
<span
onClick={this.deleteItem.bind(this, idx)}
className="delete-todo"
>
✕
</span>
<div id="todo-select" className={todo.selected && 'active'}>
<span id="todo-select-top" />
<span id="todo-select-left" />
</div>
</li>
))}
</ul>
</div>
)
}
The list item can be a stateless component, so the onSelect and onDelete become callback functions.
Deleting item with index may get you in trouble, since React will not re-render the entire list every time.
I don't know what's inside getOldTodo, but custructor cannot wait. So it will be null initially, if it's an async function.
There is an implementation using ES6 syntax.
Each list item is stateless:
const ListItem = props => {
const { todo, deleteItem, selectItem } = props;
return (
<li key={todo.key} className="todo-li-item" onClick={selectItem}>
<span className="todo-item">{todos.text}</span>
<span onClick={deleteItem} className="delet-todo">
✕
</span>
clicked
<div id="todo-select" className={'hidden'}>
<span id="todo-select-top" />
<span id="todo-select-left" />
</div>
</li>
);
};
All events are handled by a stateful component:
export class Todo extends Component {
state = {
input: '',
todos: [],
};
async componentDidMount() {
const todos = await this.getOldTodo();
this.setState({ todos });
}
render() {
return (
<div>
{this.state.todos.map(todo => (
<ListItem
todo={todo}
key={todo.key}
selectItem={() => {
this.selectItem(todo);
}}
deleteItem={() => {
this.deleteItem(todo);
}}
/>
))}
</div>
);
}
selectItem = todo => {
const idx = this.state.todos.findIndex(i => i.key === todo.key);
const todos = this.state.todos.slice();
const todo = { ...this.state.todos[idx] };
// change
todos[idx] = todo;
this.setState({
todos
});
}
deleteItem = todo => {
const idx = this.state.todos.findIndex(i => i.key === todo.key);
const todos = this.state.todos.splice(idx, 1);
this.setState({
todos
});
}
getOldTodo = async () => {
//...
}
}
Does this make sense to you?

Unable to Type and update state of input element in react

I have a text box and on typing a name i get a list of options via an api call. I then populate a list and on click of a list item i am trying to fill the text in the input box. Firstly when i add a value prop to the input element i am unable to to type anything in the text box. Also on clicking the list item the value of the text doesnt update. Can someone tell me what im doing wrong here
class AutoCompleteSearch extends Component {
constructor(props) {
super(props);
this.state = {
value: '',
suggestions: []
}
}
autoSearchInputChange(e) {
let searchValue = e.target.value;
if (!searchValue.trim()) {
this.setState({ value : '', suggestions: [] })
return ;
}
if (searchValue.length >= 3) {
setTimeout(() => {
searchDoctorByName(searchValue).then((response) => {
this.setState({ value : searchValue, suggestions: response.data })
})}, 1000);
}
}
selectItemFromList(doctorObject) {
this.setState({
value: doctorObject.name ,
suggestions: [doctorObject]
});
console.log(this.state);
}
render() {
let renderItems = () => {
let listItems = this.state.suggestions.map((suggestion, index) => {
let doctorObject = suggestion;
return (
<li onClick={() => this.selectItemFromList(doctorObject)} key={index}>
{doctorObject.name}
</li>
);
});
return (
<div>
<ul className="doctor-list">
{listItems}
</ul>
</div>
);
}
return (
<div className="form-group">
<label className="control-label form-label col-md-4" htmlFor="auto-complete-search">Doctor Name</label>
<div className="col-md-20">
<input className="custom-input"type="text" ref="test" id="auto-complete-search" required
placeholder="Enter Doctor name"
onChange={(e) => this.autoSearchInputChange(e)}
/>
{this.state.suggestions.length > 0 ? renderItems() : null}
</div>
</div>
);
}
}
export default AutoCompleteSearch;

Resources