after deletion the UI re renders and getting all the check boxes - arrays

The checkboxes and delete function working fine.. after the deletion the UI re renders and again im getting all the check boxes .. The marked checkboxes should not come again. Please suggest me how to stop this. I dont want the checked boxes in the UI after the delete button is clicked .
import React,{Component} from 'react';
import './UserList.css';
import jsonData from '../../people_(5).json';
const list = jsonData.People
const list2 = list.map(v => {
return {
...v,
value: false
}
})
list2.forEach((obj,index)=>obj.id=index)
class UserList extends Component{
state={
userList: list2
};
handleChange = e => {
console.log(this.state.userList)
const id = e.target.id;
this.setState(prevState => {
return {
userList: prevState.userList.map(
li => (li.id === +id ? {...li,
value: !li.value
} : li)
)
};
});
};
handleClick = () => {
console.log(this.state.userList)
this.setState(prevState => {
return {
userList: prevState.userList.filter(li => !li.value)
};
});
};
render(){
return(
<form className="pa4">
<fieldset id="people" className="del bn">
<legend className="fw7 mb2">People<button onClick={this.handleClick}>Delete</button></legend>
{this.state.userList.map(e => (
<div key={e.id}>
<input
type="checkbox"
id={e.id}
checked={e.value}
onChange={this.handleChange}
/>
<label htmlFor={e.name}>{e.name}</label>
</div>
))}
</fieldset>
</form>
);
}
}
export default UserList

I think the position that you put your button isn't correct.Every time click the button inside the form element will cause the page re-render,maybe you can change the button position like this
<div>
<form className="pa4">
<fieldset id="people" className="del bn">
...
</fieldset>
</form>
<button onClick={this.handleClick}>delete</button>
</div>
After the page re-render,people will be set to the initial value, so the handleChange actually doesn't work, then you can't delete the corresponding input element.

You Are not Updating the right values at the right place. Please check your logic in below code. You have un controlled checkboxes and also you are making changes to current state from prevState instead filter data by checkbox states
handleClick = () => {
console.log(this.state.userList)
this.setState(prevState => {
return {
userList: prevState.userList.filter(li => !li.value)
};
});
};

Related

How to change specific item on state of array in React?

I am creating a todolist with react and context API. As a default, when item is created "isDone" key of array item is false. When I click the completeAll button, I want to make all task's "isDone" true.
import './FormInput.scss';
import List from '../List/List';
import Footer from '../Footer/Footer';
import {MainContext, useContext} from "../../context";
function FormInput() {
const {taskList, SetTaskList} = useContext(MainContext);
const submitTask = (e) => {
e.preventDefault();
SetTaskList((prev) => [...prev,{"task":e.target.task.value,"isDone":false}])
console.log(e.target.task.value);
}
const CompleteAll = (e) =>{
SetTaskList((prev) => {
const list = prev.map((item) => item.isDone===true)
return{
list
}
})
}
return (
<div className="form-input">
<h1>TODOS</h1>
<div className="form-top">
<button id="completeAll" onClick = { e => CompleteAll(e)}>❯</button>
<form onSubmit = {(e) => submitTask(e)}>
<input type="text" name="task" id="taskInfo" placeholder="What needs to be done?"/>
</form>
</div>
<List/>
{ taskList[0] ? <Footer/> : ""}
</div>
);
}
export default FormInput;
Here is the code. I try to code completeAll function but it set the tasklist to a single true, false value.
You can use spread operator to do it.
const CompleteAll = (e) => {
SetTaskList((prev) => {
return prev.map((item) => ({ ...item, isDone: true }));
});
};

Grab input value inside array when button is clicked

I want to grab the value of input inside the array when the button is clicked. How do i pass the input value to the function of button.
Any help would be appreciated. Thanks
import React, { useState, useEffect } from 'react'
export default function Todo(props) {
const [todo,settodo] = useState([]);
function getdata(){
//fetch data
settodo(data);
}
function SaveInput(id){
}
useEffect(() => {
getdata();
},[]);
return (
<React.Fragment>
<div>
{todo.map(function(item, key){
return <div>
<div>{item.name}</div>
<div>
<input type="text" name="inputval" onChange={() => handleChange(e)}>
<button onClick={()=> SaveInput(item.id)}></button>
</div>
</div>
})}
</div>
</React.Fragment>
)
}
You need to send item.id to your handleChange function,
<input type="text" name="inputval" onChange={(e) => handleChange(e,item.id)} />
You handleChange function should,
const handleChange = (e,id) => {
let val = e.target.value;
setInputVal(prevState =>({
...prevState,
[id]:val
}))
}
You must define a state to store input values,
const [inputVal,setInputVal] = useState({});
On the click of button you can access input state,
function SaveInput(id){
console.log(inputVal[id]);
}
Demo
You can save the inputs in a separate useState when the input is being changed, which can be later retrieved easily during the button click event.
Code below is an example and is not tested, but should give you some idea how to proceed.
import React, { useState, useEffect } from 'react'
export default function Todo(props) {
const [todo,settodo] = useState([]);
const [inputVal, setInputVal] = useState({});
function getdata(){
//fetch data
settodo(data);
}
function SaveInput(id){
let inputVal = inputVal[id];
// do other stuff.
}
useEffect(() => {
getdata();
},[]);
return (
<React.Fragment>
<div>
{todo.map(function(item, key){
return <div>
<div>{item.name}</div>
<div>
<input type="text" name="inputval" onChange={(e) => setInputVal({...inputVal, [item.id]: e.target.value })}>
<button onClick={()=> SaveInput(item.id)}></button>
</div>
</div>
})}
</div>
</React.Fragment>
)
}
One common pattern is to use the handleChange(event) function on input to set a state with the current value.
const [input,setInupt] = useState("");
function handleChange(event) {
setInput(event.target.value)
}
and when the button is clicked, you can use the value of the input state to pass on
<button onClick={()=> console.log(input))}>
First of all, If you are having an onChange method then you must have a value for that input as well or else it will display a warning for "uncontrolled input" and that input box is of no use to you unless you provide a value to it.
Secondly, you should use a state for the values of those input boxes and then you can access the values of input in the save button click function. Here is the example of how you can do it.
import React from 'react'
export default class Todo extends React.Component {
constructor(props) {
super(props);
this.state = {
inputIDs: {}
}
}
SaveInput = id => {
console.log("input value:", this.state[id]);
};
handleChange = (e, id) => {
this.setState({[id]: e.target.value});
};
render() {
const {inputIDs} = this.state;
const todo = [
{id: 1, val: "abc", name: "lorem"},
{id: 2, val: "xyz", name: "Ipsum"}
];
let todos = todo.map((item, key) => {
return <div key={key}>
<div>{item.name}</div>
<div>
<input type="text" value={this.state[item.id]} onChange={(e) => this.handleChange(e, item.id)}/>
<button onClick={() => this.SaveInput(item.id)}>Click Me!</button>
</div>
</div>
});
return (
<React.Fragment>
{todos}
</React.Fragment>
)
}
}

edit todo list state not passed correctly in react with axios

I have a component that lists the current todo's which you can add your own todo's and delete todo's, which is working 100%. The only issue I am facing is updating my current todo's. I have added the necessary code below, help would be appreciated.
I am using proptypes and can see I have warning in my console which I suspect might be the problem. Here is the warning:
Warning: Failed prop type: The prop `editTodo` is marked as required in `TodoItem`, but its value is `undefined`.
in TodoItem (at Todos.js:10)
in Todos (at App.js:83)
in section (at App.js:82)
in Route (at App.js:75)
in main (at App.js:73)
in div (at App.js:72)
in Router (created by BrowserRouter)
in BrowserRouter (at App.js:71)
in App (at src/index.js:7)
Here is my edit todo button:
<div id="card-item-edit">
<button
className="edit-btn"
onClick={() => this.toggleForm()}>
EDIT
</button>
{this.showEditTodoForm()}
</div>
In my edit todo button I have assigned a toggle function onClick which opens input field if true:
toggleForm = () => {
if (!this.state.isShowing) {
this.setState({ isShowing: true });
} else {
this.setState({ isShowing: false });
}
}
Which passes state and opens this form onClick
showEditTodoForm = () => {
if(this.state.isShowing) {
return(
<div>
<form onSubmit={this.handleFormSubmit}>
<input
type="text"
name="edit todo"
placeholder="Edit Your Todo"
value={this.state.value}
onChange={this.onChange}
/>
</form>
</div>
)
}
}
onSubmit the value is updated with Axios. I think I might be doing something wrong here, I tried testing with Postman but just can't get it working, here is my handleFormSubmit function:
handleFormSubmit = (id) => {
const title = this.state.title;
event.preventDefault();
axios.put(`http://localhost:3004/todos/${id}`,
{
title
},
)
.then(() => {
})
.catch(error => console.log(error))
}
I am also using the onChange property in the form submit, here is the function:
onChange = (e) =>
this.setState({
[e.target.name]: e.target.value // demo react tools to show what happens when value changes when typing
}
);
Managed to resolve this with online mentor from Codementor, would highly recommend this resource for anyone stuck with problems like these. Here is the solution:
Edit todo form, using ref's to pass state:
showEditTodoForm = () => {
const { title} = this.props.todo
if(this.state.isShowing) {
return(
<div>
<form ref={this.formRef} onSubmit={this.handleFormSubmit}>
<input
type="text"
name="title"
placeholder="Edit Your Todo"
defaultValue={title}
/>
<input type="submit" value="Save" />
</form>
</div>
)
}
}
handleFormSubmit = (e) => {
e.preventDefault()
const title = this.formRef.current['title'].value
const { id } = this.props.todo
this.props.editTodo(id, title)
}
Then I am using proptypes to pass to my main component:
editTodo = (id, title) => {
axios.put(`http://localhost:3004/todos/${id}`,
{
title
},
)
.then(({data}) => {
this.setState(prevSate => {
const { todos } = prevSate;
const oldTodoIndex = todos.findIndex(todo => todo.id === data.id )
const newTodo = {...todos[oldTodoIndex], ...data}
todos.splice(oldTodoIndex, 1, newTodo)
return {todos: todos}
})
})
.catch(error => console.log(error))
}

How do I render two inputs into one list in Reactjs

I'm trying to teach myself how to code and created a little todo app. In the rendering of each todo input I have the element and then a checkbox to click for it to be removed. I tried to create a separate input to give the amount of time it will take for each item to be created. When I tried to link that up to my rendering method, nothing renders and I have zero error messages.
import React from 'react';
class InputBar extends React.Component {
state={ todo: '',
time: null
}
onInputSubmit = e =>{
e.preventDefault();
this.props.todoSubmit(this.state.todo)
this.props.timeSubmit(this.state.time)
this.setState({
todo: '',
time: this.state.time
})
}
render() {
return (
<div className="input-group mb-3">
<form onSubmit={ this.onInputSubmit } >
<label>Input Todo</label>
<div className='input-control'>
<input
type='text'
className="form-control"
aria-label="Sizing example input"
aria-describedby="inputGroup-sizing-default"
value={this.state.todo}
onChange={e => this.setState({
todo: e.target.value
})}
/>
<input
type='number'
required
className='input-control'
defaultValue={0}
value={this.state.time}
placeholder='How long will it take?'
onChange={e => this.setState({
time: e.target.value
})} />
</div>
</form>
</div>
)
}
}; export default InputBar
import React from 'react';
import InputBar from './inputbar';
class List extends React.Component {
state = {
list: [],
nextId: 1
};
componentDidMount() {
const list = JSON.parse( localStorage.getItem( "list" ) );
this.setState( { list } );
}
addToList = (todo, time, list) => {
this.setState({
list: [
{
name: todo,
text: time,
id: this.state.nextId
},
...this.state.list,
],
nextId: this.state.nextId + 1
},
() => {
localStorage.setItem("list", JSON.stringify(this.state.list));
});
}
removeFromList = (id) => {
this.setState({
list: this.state.list.filter(entry => entry.id !== id )
},
() => {
localStorage.setItem("list", JSON.stringify(this.state.list));
}
);
}
renderList = () => {
return this.state.list.map((element) => {
return (
<div>
<li>
{element.name}
<input
style={{marginLeft: '15px'}}
type='checkbox'
onClick={()=> this.removeFromList(element.id)}
/>
</li>
</div>
)
})
}
render() {
console.log(this.state.todo, this.state.time)
return (
<div>
<InputBar
todoSubmit={this.addToList}
timeSubmit={this.addToList}
/>
<ul>
{ this.renderList() }
</ul>
</div>
)
}
};
export default List;
//this is then send to imported an app component to be rendered
Hi & welcome to Stack Overflow, Elias.
You pass two handlers to your InputBar component that both resolve to the addToList handler defined in your list component. However, when you call these handlers, the arguments do not match what addToList is expecting, which is a list, a todo and a time.
You obviously don't need a list argument (it's never used in addToList as you manage the list present in that component state's anyway, which is fine), so list can be removed.
And in my opinion, you do not need 2 handlers (one for the todo, one for the time value). One handler that adds both todo AND time would be better (after all, the idea is to submit a todo as a whole object) and would line up with what addToList would expect.
In summary, here are the changes I suggest:
In inputbar.js:
onInputSubmit = e => {
e.preventDefault();
const { todo, time } = this.state
this.props.handleSubmit(todo, time)
this.setState({
todo: '',
time: this.state.time
})
}
In your List component:
addToList = (todo, time) => {
// just removed the unnecessary 'list' param
// actual code left untouched
}
// other code
render() {
console.log(this.state.todo, this.state.time)
return (
<div>
<InputBar handleSubmit={this.addToList} />
<ul>
{ this.renderList() }
</ul>
</div>
)
}

Calling a function for a generate button

I have a page where a user can search a database for a given condition, then the data is returned with another button that the user can use to add information back to the database. However whenever I click on the second button, the page reloads. I can't get so much as a console.log to go in. I'm new to react and could use any help at all.
import React , { Component } from 'react';
import { database } from '../firebase';
const byPropKey = (propertyName, value) => () => ({
[propertyName]: value,
});
class Search extends Component{
constructor(props) {
super(props);
this.state={
users: null,
searchCondition: "",
friend: ""
}
// this.setState = this.setState.bind(this);
}
onSubmit = (event) => {
let {
searchCondition,
friend
} = this.state;
database.searchConditions(searchCondition).then(snapshot =>
this.setState(() => ({ users: snapshot.val() }))
);
event.preventDefault();
}
messageSubmit = (event) => {
console.log("Click")
}
render(){
let {
users,
searchCondition,
friend
} = this.state;
return(
<div>
<h1>Search for conditions</h1>
<form onSubmit={this.onSubmit}>
<div className="search">
<input
value={searchCondition}
onChange={event => this.setState(byPropKey('searchCondition', event.target.value))}
type="text"
placeholder="Condition to Search For"
/>
<button className="friendButton"
onClick="x"
type="submit">
Search
</button>
</div>
</form>
{!!users && <UserList users={users} />}
</div>
)
}
}
let UserList = ({ users, message }) =>
<div>
<h2>List of Usernames and Conditions of your Search</h2>
{Object.keys(users).map(key =>
<div key={key}>{users[key].username} : {users[key].condition}
<form>
<div className="search">
<input
value={message}
onChange={console.log("test")}
type="text"
placeholder="Message for this User"
/>
<button className="messageButton"
onClick={console.log(message)}
type="submit">
Message
</button>
</div>
</form>
</div>
)}
</div>
export default Search;
Have you tried to place the event.preventDefault() at the beginning of the event handler?
It should prevent the default behaviour imediately as the event gets fired.
Hope it works!
a couple things i can see, youre even.preventDefault() should be at the top of the page, you said it was reloading so thats unwanted behavior. second you should set state within the then, generally speaking in my experience that doesnt work- i believe due to setState being asynchronous or something of that nature.
i would rewrite your submit like this
onSubmit = (event) => {
event.preventDefault();
let {
searchCondition,
friend
} = this.state;
let value;
database.searchConditions(searchCondition).then(snapshot =>
value = snapshot.val
);
this.setState(() => ({ users: value) }))
}
also likely the reason your "messageSubmit()" was not console logging is because youre using a submit handler not a click handler so everytime your clicked you were reloading the page.
cheers

Resources