My handleChange method of simple react todoapp is not doing what it needs to do - javascript

In the output, Only the default completed values are checked! not able change the checks of tasks.
These are my Java script files
app.js
class App extends Component {
constructor() {
super()
this.state = {
todos: Todosdata
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(id) {
this.setState(prevState => {
const udpated = prevState.todos.map(todo => {
if (todo.id === id) {
todo.completed = !todo.completed
}
return todo
})
return {
todos: udpated
}
})
}
render() {
const todoelements = this.state.todos.map(item => <ToDoItem key={item.id}
todoitem={item}
handleChange={this.handleChange} />)
return (
<div className="App">
< div className="todo-list" >
{todoelements}
</div >
</div>
)
}
}
ToDoItem.js
const ToDoItem = (props) => {
const Afterstyle = {
fontColor: "red",
textDecoration: "line-through"
}
return (
<div className="todo-item">
<input type="checkbox"
checked ={props.todoitem.completed}
onChange ={() => props.handleChange(props.todoitem.id)} />
<p style={props.todoitem.completed ? Afterstyle : null}>{props.todoitem.task}</p>
</div>
)
}
i did console log inside if condition of handle Change method,its printing 2 times.
I am stuck at this for hours please fix this!

You are mutating the todo item instead of creating a new one. Change your handler like that:
handleChange(id) {
this.setState(prevState => {
const udpated = prevState.todos.map(todo => {
if (todo.id === id) {
// if the id matches return a new object
return {...todo, completed: !todo.completed};
}
return todo
});
return {todos: udpated};
}
}
Live Demo:

Related

React class component. Todo app. How to store data with localStorage

I'd like to store todo data with localStorage so that it won't disappear after refreshing the page.
I used React class component when started creating.
I've added 'handleFormSubmit' and 'ComponentDidMount' methods.
nothing stores in localStorage when I type todo and choose date.
get an error in ComponentDidMount with
Line 'const result = localData ? JSON.parse(localData) : [];'
:SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data
how can I set and get items?
It would be really appreciated if I could get help.
I'd like to make this app really work.
import React from "react"
import TodoItem from "./components/TodoItem"
import todosData from "./components/todosData"
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
todos: todosData,
//setTodos: todosData,
newItem: "",
deadline: "",
editing: false
}
this.handleChange = this.handleChange.bind(this)
this.addTodo = this.addTodo.bind(this)
this.updateInput = this.updateInput.bind(this)
this.deleteItem = this.deleteItem.bind(this)
this.updateItem = this.updateItem.bind(this)
this.updateDeadline = this.updateDeadline.bind(this)
this.updateInputDeadline = this.updateInputDeadline.bind(this)
this.editItem = this.editItem.bind(this)
this.handleFormSubmit = this.handleFormSubmit.bind(this)
}
handleChange(id) {
this.setState((prevState) => {
const updatedTodos = prevState.todos.map((todo) => {
if (todo.id === id) {
return { ...todo, completed: !todo.completed };
} else {
return todo;
}
});
return { todos: updatedTodos };
});
}
addTodo(e) {
e.preventDefault();
const newTodo = {
id: this.state.todos.length + 1,
text: this.state.newItem,
completed: false,
deadline: this.state.deadline
}
const newTodos = this.state.todos.concat([newTodo]);
this.setState({
todos: newTodos
})
}
updateInput(value, id) {
this.setState((prevState) => {
const updatedTodos = prevState.todos.map((todo) => {
if(todo.id === id) {
return {...todo, text: value}
}else {
return todo;
}
})
return {todos: updatedTodos}
})
}
updateInputDeadline(value, id) {
this.setState((prevState) => {
const updatedTodos = prevState.todos.map((todo) => {
if(todo.id === id) {
console.log(value, id);
return {...todo, deadline: value}
}else {
return todo;
}
})
return {todos: updatedTodos}
})
}
updateItem(e) {
this.setState({
newItem: e.target.value
})
}
updateDeadline(e) {
this.setState({
deadline: e.target.value
})
}
deleteItem(id){
const filteredItems= this.state.todos.filter(item =>
item.id!==id);
this.setState({
todos: filteredItems
})
}
editItem(id) {
this.setState({
editing: id
})
}
handleFormSubmit() {
const { todo, deadline } = this.state;
localStorage.setItem('todo', JSON.stringify(todo));
localStorage.setItem('deadline', deadline);
};
componentDidMount() {
const localData = localStorage.getItem('todo');
const result = localData ? JSON.parse(localData) : [];
const deadlineData = localStorage.getItem('deadline');
this.setState({ result, deadlineData });
}
render() {
const todoItems = this.state.todos.map
(item =>
<TodoItem
key={item.id}
item={item}
handleChange={this.handleChange}
addTodo={this.addTodo}
deleteItem={this.deleteItem}
updateInput={this.updateInput}
updateInputDeadline={this.updateInputDeadline}
isEdited={this.state.editing === item.id}
editItem={this.editItem}
/>)
return (
<div className="todo-list">
<Timer />
<form onSubmit={this.handleFormSubmit}>
<div className="add-todo">
<label>Add an item...</label>
<input
type="text"
name="todo"
placeholder="Type item here..."
value={this.state.newItem}
onChange={this.updateItem}
/>
</div>
<div className="date">
<label htmlFor="deadline">Deadline</label>
<input
type="date" id="start" name="deadline"
min="2021-01-01"
max="2024-12-31"
value={this.state.deadline}
onChange={this.updateDeadline}
/>
</div>
<button type="submit" onClick={this.addTodo}>Add to the list</button>
</form>
{todoItems.length === 0 ? <p>No items</p> : null}
<div className="todoitems">
{todoItems}
</div>
</div>
)
}
}
export default App
When you press the button, there are two events that you are trying to call - addTodo and handleFormSubmit. Since you are calling e.preventDefault() in addTodo, the submit event is never called. You could do all of the actions you need in one of the methods.
My guess is that you are either trying to JSON.parse an array instead of an object, or the value of todo is undefined. You are trying to get todo out of this.state, but you only have todos in your state, so it might be a typo. The same goes for deadline.
You are doing the setting and getting correctly. You could actually get data from localStorage even when you are first setting the state in constructor. But the componendDidMount approach you tried is also good.
constructor(props) {
super(props)
const cachedTodos = localStorage.getItem("todo")
this.state = {
todos: cachedTodos ?? todosData,
...
}

React setState not updating the state

I'm new to React and I'm having some trouble with changing sate with setState method. I'm trying to update state of ToDo item when I click on the checkbox. When I'm debugging handleChange method it changes the state of the item for a while. Unfortunatelly, the final state is like at the beginning (state of the checkbox is never changed). Does somebody know where the problem is?
class App extends React.Component {
constructor() {
super()
this.state = {
todos: todosData,
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(id){
this.setState(prevState => {
const updatedTodos = prevState.todos.map(todo => {
if (todo.id === id) {
todo.completed = !todo.completed
}
return todo
})
return {
todos: updatedTodos
}
})
}
render() {
const todoItems = this.state.todos.map(item => <ToDoItem key={item.id} todoItem={item}
handleChange={this.handleChange}/>)
return (
<div className="App">
{todoItems}
</div>
);
}
}
export default App;
Here is my ToDoItem component:
import React from 'react'
function ToDoItem(props){
return (
<div className = "todo-list">
<div className = "todo-item">
<input type="checkbox"
checked={props.todoItem.completed}
onChange = {() => props.handleChange(props.todoItem.id)}/>
<p>{props.todoItem.text}</p>
</div>
</div>
)
}
export default ToDoItem
Just use this handler and let me know if works.
handleChange(id){
const list = this.state.todos.slice();
const index = list.findIndex(o => o.id === id);
list[index].completed = !list[index].completed;
this.setState({ todos: list });
}
I think the problem is here:
handleChange(id){
this.setState(prevState => {
const updatedTodos = prevState.todos.map(todo => {
if (todo.id === id) {
todo.completed = !todo.completed
}
return { ...todo }; // need to return a new object
})
return {
todos: updatedTodos
}
})
}
todos are objects and event if todos are being set as a new object every time, its items referencing to the same object, and it is used by ToDoItem.

todo list app - onChange on ReactJS doesn't work?

I am new to ReactJS and I'm doing a simple to-list with it. I am working on the part of check box. What I want to do is to uncheck the box whenever i click it or vice versa. But nothing has been changed, so I am wondering if anything wrong....
Below is the App.js and index.js file
import React from 'react';
import Todolist from './Todolist';
import todoData from './todoData';
class App extends React.Component {
constructor(){
super()
this.state = {
todos: todoData //grab the raw data
}
this.handleChange = this.handleChange.bind(this)
}
handleChange (id){
console.log(id)
this.setState (prevState => {
const updatedtodos = prevState.todos.map(todo => {
if(todo.id == id) {
todo.completed = !todo.completed
}
return todo
})
return {
todos: updatedtodos
}
})
}
render() {
const TodoItem = this.state.todos.map(item => <Todolist key={item.id} item={item} handleChange={this.handleChange}/>)
return(
<div>
{TodoItem}
</div>
);
}
}
export default App;
import React from 'react';
function Todolist (props){
return(
<div className="wholelist">
<input
type="checkbox"
checked={props.item.completed}
onChange={()=> props.handleChange(props.item.id)}
/>
<label className="items">{props.item.name}</label>
</div>
);
}
export default Todolist;
Change handleChange:
handleChange(id) {
console.log(id);
const updatedtodos = this.state.todos.map(todo => {
if (todo.id === id) {
todo.completed = !todo.completed;
}
return todo;
});
this.setState({
todos: updatedtodos
});
}
you need to change two things.
firstly:
handleChange = (id) => { // I've changed this to an arrow function so it can access parent scope and you can use "this" keyword
console.log(id)
this.setState (prevState => {
const todos = this.state.todos.map(todo => {
if(todo.id === id) {
todo.completed = !todo.completed
}
return todo
})
this.setState({ todos }) // you can use object shorthand here to change todos in state to use the todos set above
})
}
secondly:
change this: handleChange={this.handleChange}
to: handleChange={() => this.handleChange(item.id)}
you said that handleChange(id) need to take an id, so we need to pass one in when we call it
let me know if that works!

Changing state of child component from parent with refs

I have a TaskList. I'm trying to display a child component TaskEdit (edit box for the task) several components lower, onClick of a button.
TaskEdit has a state variable hidden, set to true by default. How do I change the state inside the child component from within the child component?
React documentation mentions a forwardRef() function in order to reference the child component, but I haven't any luck so far with it.
Here's my code:
import TaskEdit from '../TaskEdit';
class TasksBase extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
tasks: [],
};
this.markTaskAsCompleted = this.markTaskAsCompleted.bind(this);
this.deleteTask = this.deleteTask.bind(this);
this.incrementDate = this.incrementDate.bind(this);
this.toggleTaskEdit = this.toggleTaskEdit.bind(this);
}
componentDidMount() {
this.onListenForTasks();
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
const { tasks, loading } = this.state;
return (
<div>
{loading && <div>Loading ...</div>}
{tasks ? (
<TaskList tasks={tasks}
handleToggleEdit={this.toggleTaskEdit}
handleMarkCompleted={this.markTaskAsCompleted}
handleDeleteTask={this.deleteTask}
taskEditElement={this.editTaskElement}
/>
):(
<div>There are no tasks ...</div>
)}
</div>
);
}
onListenForTasks() {
this.setState({ loading: true });
this.unsubscribe = this.props.firebase
.tasks()
.orderBy('importance', 'desc')
.orderBy('urgency', 'desc')
.orderBy('created', 'desc')
.onSnapshot(snapshot => {
if (snapshot.size) {
let tasks = [];
snapshot.forEach(doc =>
tasks.push({ ...doc.data(), uid: doc.id }),
);
this.setState({
tasks: tasks,
loading: false
});
} else {
this.setState({ tasks: null, loading: false });
}
});
}
markTaskAsCompleted(task){
// Some code
}
deleteTask(id){
// Some code
}
toggleEditTask(){
this.editTaskElement.current.toggle();
}
}
const Tasks = withFirebase(TasksBase);
const TaskList = ({ tasks, handleToggleEdit, handleMarkCompleted, handleDeleteTask }) => (
<ul className="tasks">
{tasks.map( task => (
<Task key={task.uid}
task={task}
handleToggleEdit={handleToggleEdit}
handleMarkCompleted={handleMarkCompleted}
handleDeleteTask={handleDeleteTask}
/>
))}
</ul>
);
const Task = ({ task, handleToggleEdit, handleMarkCompleted, handleDeleteTask}) => (
(!task.completed && !task.obsolete && !task.waitingForDependencies) && (!task.start || task.start.toMillis() <= Date.now()) &&
<li className="task">
<strong>{task.userId}</strong> {task.name}
{ task.estimate &&
<span>{task.estimate.amount + task.estimate.unit}</span>
}
<div className="icons">
<FontAwesomeIcon icon="edit" onClick={() => handleToggleEdit(task)} />
<FontAwesomeIcon icon="check-circle" onClick={() => handleMarkCompleted(task)} />
<FontAwesomeIcon icon="times-circle" onClick={() => handleDeleteTask(task.uid)}/>
</div>
<TaskEdit task={task} />
</li>
);
const condition = authUser => !!authUser;
export default compose(
withEmailVerification,
withAuthorization(condition),
)(Tasks);
TaskEdit:
import React, { Component } from 'react';
import { withFirebase } from '../Firebase';
const INITIAL_STATE = {
hidden: true,
};
class TaskEditBase extends Component {
constructor(props) {
super(props);
this.state = { ...INITIAL_STATE };
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
console.log("name " + name);
console.log("value " + value);
this.setState({
[name]: value
});
}
toggle(){
this.setState({
hidden: !this.prevState.hidden
})
}
onSumbitTaskEdit = (event) => {
event.preventDefault();
};
render(){
return(
!this.state.hidden &&
<div className="task-edit">
<form onSubmit={this.onSumbitTaskEdit}>
<div>A bunch of inputs</div>
<input type="submit" value="Edit"/>
</form>
</div>
)
}
}
const TaskEdit = withFirebase(TaskEditBase);
export default TaskEdit;

react js delete handler removes the 1st index in the array

The problem is any item button click will delete the 1st index item in the array.
I looked at these resources on handling deleting an item in an array in react.
How to remove item in todo list using React
Removing element from array in component state
React Binding Patterns
I've tried changing how my handler is called in TodoList and TodoItemLIst and that causes the handler not to fire on click. I've tried different methods of binding the handler - adding a param has no effect on it -bind(this) breaks it & isn't necessary because I'm using a function.
I've tried setting state different ways using a filter method. No change happens...
this.setState((prevState) => ({
todoItems: prevState.todoItems.filter(i => i !== index)
}));
I'm not understanding where/what the problem is.
class App extends Component {
constructor(props) {
super(props);
this.state = {
value: '',
listItemValue: props.value || '',
todoItems: [
{id: _.uniqueId(), item: 'Learn React.'},
{id: _.uniqueId(), item: 'Improve JS skills.'},
{id: _.uniqueId(), item: 'Play with kittens.'}
]
};
}
handleChange = (event) => {
let value = event.target.value;
this.setState({
value: this.state.value,
listItemValue: value
});
}
handleSubmit = (event) =>{
event.preventDefault();
this.setState({
value: '',
listItemValue: ''
});
}
addTodoItem = () => {
let todoItems = this.state.todoItems.slice(0);
todoItems.push({
id: _.uniqueId(),
item: this.state.listItemValue
});
this.setState(prevState => ({
todoItems: [
...prevState.todoItems,
{
id: _.uniqueId(),
item: this.state.listItemValue
}]
}))
};
deleteTodoItem = (index) => {
let todoItems = this.state.todoItems.slice();
todoItems.splice(index, 1);
this.setState({
todoItems
});
}
render() {
return (
<div className="App">
<h1>Todo List</h1>
<TodoListForm name="todo"
onClick={ ()=>this.addTodoItem() }
onSubmit={ this.handleSubmit }
handleChange={ this.handleChange }
value={ this.state.listItemValue } />
<TodoList onClick={ ()=>this.deleteTodoItem() }
todoItems={ this.state.todoItems }/>
</div>
);
}
}
const TodoList = (props) => {
const todoItem = props.todoItems.map((todo) => {
return (
<TodoListItem onClick={ props.onClick }
key={ todo.id }
id={ todo.id }
item={ todo.item }/>
);
});
return (
<ul className="TodoList">
{todoItem}
</ul>
);
}
const TodoListItem = (todo, props) => {
return (
<li className="TodoListItem">
<div className="TodoListItem__Item">{todo.item}
<span className="TodoListItem__Icon"></span>
<button onClick={ todo.onClick }
type="button"
className="TodoListItem__Btn">×</button>
</div>
</li>
)
};
In the deleteTodoItem method, try just
let todoItems = this.state.todoItems.slice(0, -1);
and remove the call to splice().

Categories