I'm trying to make an event on todoList to check out the box, the event is called checkBox in my code, but I'm stuck and cannot figure it out. I'm very new to Javascript and React, could you please help me. I think there is something wrong here:
{this.state.itemList.map((item, index) => (
{item.todo}{item.completed}
checkBox={this.checkBox}
this.checkBox(item.completed)}
/>
))}
I cannot quite figure it out yet, here is my full code
import React, { Component } from 'react'
export default class TodoList extends Component {
constructor(props) {
super(props)
this.state = {
todo:"",
completed: "",
itemList: [
{ todo: "Take out the Trash", completed: true },
{ todo: "Water the plants", completed: false },
{ todo: "Grocery shopping", completed: true }
]
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
this.checkBox = this.checkBox(this)
}
handleChange(e) {
this.setState({todo: e.target.value});
}
handleSubmit(n) {
this.setState({
itemList: [...this.state.itemList, {todo: this.state.todo, completed: false}],
});
}
checkBox(event) {
this.setState(prev =>{
const newList = prev.itemList.map(todo => {
if (todo.event === todo) {
todo.completed = !todo.completed
}
return todo
})
return {itemList: newList
}
})
}
render() {
return (
<div className="container">
<div className="main">
<div>
<input className="header w-50 p-2" type="text" placeholder="enter task" value={this.state.todo} onChange={this.handleChange}/><br></br>
<button className="button btn-btn-primary ml-1 mt-3" onClick={this.handleSubmit}>Submit</button>
</div>
<div>
{this.state.itemList.map((item, index) => (<p className="mt-4 list" key={index}>{item.todo}{item.completed} checkBox={this.checkBox} <input type="checkbox" onChange={()=>this.checkBox(item.completed)}/></p>))}
</div>
</div>
</div>
)
}
}
You did not bind the checkBox function properly in the constructor, I fixed it below and commented where the issue was:
constructor(props) {
super(props)
this.state = {
todo:"",
completed: "",
itemList: [
{ todo: "Take out the Trash", completed: true },
{ todo: "Water the plants", completed: false },
{ todo: "Grocery shopping", completed: true }
]
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
this.checkBox = this.checkBox.bind(this)// correctly bound function, instead of 'this.checkBox = this.checkBox(this)'
}
Related
I am displaying data in the checkboxlist. I have implemented Select All and ClearAll buttons that selects all or unselect all checkboxes.
Please find my react code in the sandbox: https://codesandbox.io/s/immutable-pond-07qnue
Please fix my code why SelectAll and ClearAll functionality is not working.
import React from "react";
const data = [
{ templateID: "11", templateName: "All" },
{ templateID: "21", templateName: "SC" }
];
export class Delete_Item extends React.Component {
constructor(props) {
super(props);
this.state = {
Template_ID: "",
TemplateName: "",
Templatelist: [],
checkedItems: []
};
this.handleChange = this.handleChange.bind(this);
this.handleSelectAll = this.handleSelectAll.bind(this);
this.handleClearAll = this.handleClearAll.bind(this);
}
componentDidMount() {
this.getTemplateList();
}
getTemplateList() {
this.setState({
Templatelist: data,
TemplateName: data[0].templateName,
loading: false
});
console.log(this.state.Templatelist);
}
handleChange(event) {
const id = parseInt(event.target.id, 10);
const index = this.state.checkedItems.indexOf(id);
const updatedArray = [...this.state.checkedItems];
if (index !== -1) {
updatedArray.splice(index, 1);
} else {
updatedArray.push(id);
}
this.setState((prevState) => ({
checkedItems: updatedArray
}));
console.log(this.state.checkedItems);
}
handleSelectAll() {
return this.setState({
checkedItems: this.state.Templatelist.map((id) => id)
});
}
handleClearAll() {
return this.setState({ checkedItems: [] });
}
render() {
return (
<div>
<ul style={{ listStyle: "none" }}>
{this.state.Templatelist.map((item, index) => {
return (
<li key={item.templateID}>
<input
type="checkbox"
id={item.templateID}
value={item.templateName}
onChange={this.handleChange}
/>
{item.templateName}
</li>
);
})}
</ul>
<input
type="button"
name="SelectAll"
value="Select All"
onClick={this.handleSelectAll}
/>
<input
type="button"
name="UnSelectAll"
value="Clear All"
onClick={this.handleClearAll}
/>
</div>
);
}
}
export default Delete_Item;
You need to add the checked property to your checkbox based on what you have selected this.state.checkedItems. Also in handleChange method you convert it to an integer and stored while in handleSelectAll method you use the full object. So need to make sure to store at the same type.
import React from "react";
const data = [
{ templateID: "11", templateName: "All" },
{ templateID: "21", templateName: "SC" }
];
export class Delete_Item extends React.Component {
constructor(props) {
super(props);
this.state = {
Template_ID: "",
TemplateName: "",
Templatelist: [],
checkedItems: []
};
this.handleChange = this.handleChange.bind(this);
this.handleSelectAll = this.handleSelectAll.bind(this);
this.handleClearAll = this.handleClearAll.bind(this);
}
componentDidMount() {
this.getTemplateList();
}
getTemplateList() {
this.setState({
Templatelist: data,
TemplateName: data[0].templateName,
loading: false
});
}
handleChange(event) {
const id = event.target.id;
const index = this.state.checkedItems.indexOf(id);
const updatedArray = [...this.state.checkedItems];
if (index !== -1) {
updatedArray.splice(index, 1);
} else {
updatedArray.push(id);
}
this.setState({
checkedItems: updatedArray
});
}
handleSelectAll() {
this.setState({
checkedItems: this.state.Templatelist.map((id) => id.templateID)
});
}
handleClearAll() {
this.setState({ checkedItems: [] });
}
render() {
return (
<div>
<ul style={{ listStyle: "none" }}>
{this.state.Templatelist.map((item, index) => {
return (
<li key={item.templateID}>
<input
type="checkbox"
id={item.templateID}
value={item.templateName}
checked={this.state.checkedItems.some(
(i) => i === item.templateID
)}
onChange={this.handleChange}
/>
{item.templateName}
</li>
);
})}
</ul>
<input
type="button"
name="SelectAll"
value="Select All"
onClick={this.handleSelectAll}
/>
<input
type="button"
name="UnSelectAll"
value="Clear All"
onClick={this.handleClearAll}
/>
</div>
);
}
}
export default Delete_Item;
Sample working at https://codesandbox.io/s/prod-darkness-l2vx52
I'm following Bob Ziroll's free scrimba course on React.
Thing is, my code is the same with his and it has been working so far...
but it isn't working anymore.
Here's my code
App.js
import React, { Component } from "react";
import TodoItem from "./components/TodoItem";
import todosData from "./components/todosData";
class App extends Component {
constructor() {
super()
this.state = {
todos: todosData
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(id) {
this.setState(prevState => {
console.log("PrevState Start ", prevState.todos )
const updatedTodos = prevState.todos.map(todo => {
if (todo.id === id) {
todo.completed = !todo.completed
}
return todo
})
return {
todos: updatedTodos
}
})
console.log("Changed", id)
}
render() {
const todoItem = this.state.todos.map(x =>
<TodoItem handleChange = {this.handleChange}
key={x.id}
item={x}
/>
)
return (
<div>
{todoItem}
</div>
);
}
}
export default App;
And here's the code for TodoItem.js
import React from 'react'
function TodoItem(props) {
return (
<div>
<input type='checkbox' checked={props.item.completed} onChange={() => props.handleChange(props.item.id)} />
<p>{props.item.text}</p>
</div>
)
}
export default TodoItem
And here's todosData.js*
const todosData = [
{
id: 1,
text: "Take out the trash",
completed: true
},
{
id: 2,
text: "Grocery shopping",
completed: false
},
{
id: 3,
text: "Clean gecko tank",
completed: false
},
{
id: 4,
text: "Mow Lawn",
completed: true
},
{
id: 5,
text: "Catch up on arrested development",
completed: false
}
]
export default todosData
I've tried using a callback but it isn't working. I've checked prevState and the updated state, but no change is reflected.
I'd appreciate your help on this.
When you are updating the state using setState, You should return new object to tell react that this is the change and React will update it accordingly.
Live Demo
React will compare the reference of two objects and if you won't return new object then react will take as a same object. React won't figure out when you won't return new object. You are just updating a property of an object as :
if (todo.id === id) {
todo.completed = !todo.completed
}
You just have to make a small change as:
if (todo.id === id) {
return { ...todo, completed: !todo.completed };
}
Being super curious, I tried your code and it works perfectly for me. Is this not what you wanted to happen? Check the snippet below:
document.onreadystatechange = () => {
const { useState, Component } = React;
class App extends Component {
constructor() {
super();
this.state = {
todos: todosData,
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(id) {
this.setState((prevState) => {
console.log("PrevState Start ", prevState.todos);
const updatedTodos = prevState.todos.map((todo) => {
if (todo.id === id) {
todo.completed = !todo.completed;
}
return todo;
});
return {
todos: updatedTodos,
};
});
console.log("Changed", id);
}
render() {
const todoItem = this.state.todos.map((x) => (
<TodoItem handleChange={this.handleChange} key={x.id} item={x} />
));
return <div>{todoItem}</div>;
}
}
function TodoItem(props) {
return (
<div>
<input
type="checkbox"
checked={props.item.completed}
onChange={() => props.handleChange(props.item.id)}
/>
<p>{props.item.text}</p>
</div>
);
}
const todosData = [
{
id: 1,
text: "Take out the trash",
completed: true,
},
{
id: 2,
text: "Grocery shopping",
completed: false,
},
{
id: 3,
text: "Clean gecko tank",
completed: false,
},
{
id: 4,
text: "Mow Lawn",
completed: true,
},
{
id: 5,
text: "Catch up on arrested development",
completed: false,
},
];
ReactDOM.render(<App />, document.querySelector("#root"));
};
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="root"></div>
I started to learn React and I'm following a tutorial. I'm trying to change the checkboxes when it's clicked. I checked if handleChange function before writing the setState code and it's working. I did everything as in the tutorial but when I click to checkboxes, they are not changing. Looks like I'm missing something. Here is the code:
App.js :
import React, { Component } from "react";
import TodoItem from "./TodoItem";
import todosData from "./todosData";
class App extends 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} item={item} handleChange={this.handleChange} />
));
return (
<div className="todo-list">
{todoItems}
</div>
)
}
}
export default App;
TodoItem.js :
import React from "react";
import "./index.css";
function TodoItem(props) {
return (
<div className="todo-item">
<input
type="checkbox"
checked={props.item.completed}
onChange={() => props.handleChange(props.item.id)}
/>
<p>{props.item.text}</p>
</div>
);
}
export default TodoItem;
todosData.js:
const todosData = [
{
id: 1,
text: "Take out the trash",
completed: true
},
{
id: 2,
text: "Grocery Shopping",
completed: false
},
{
id: 3,
text: "Clean gecko tank",
completed: false
},
{
id: 4,
text: "Mow lawn",
completed: true
},
{
id: 5,
text: "Catch up on Arrested Development",
completed: false
}
]
export default todosData
Actually the problem is you're mutating the callback parameter in handleChange, it's generally highly recommended to avoid mutating them cause they lead to unexpected behavior. I'd rewrite like below:
class App extends 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) return { ...todo, completed: !todo.completed };
return todo;
});
return {
todos: updatedTodos
};
});
}
render() {
const todoItems = this.state.todos.map(item => (
<TodoItem key={item.id} item={item} handleChange={this.handleChange} />
));
return <div className="todo-list">{todoItems}</div>;
}
}
You can check the result here: https://xd5w9.codesandbox.io/
I've been working on understanding React concepts and did my Todo project. I have the dummy data displaying, but can't add a new value to my dummy data, which is stored in an array of objects in a separate file, todos.js.
Here is the file hierarchy
Here is the error I am getting -
index.js:2177 Warning: Each child in an array or iterator should have a unique "key" prop.
TodoList.js
import React from 'react';
import Todo from './Todo';
import todos from '../todos'
class TodoList extends React.Component {
constructor() {
super();
this.state = {
todoItems: todos,
newItem: {}
}
}
addItem = (event) => {
event.preventDefault();
const todoList = this.state.todoItems;
todoList.push(this.state.newItem);
this.setState({
todoList: todos,
newItem: {}
});
};
handleInput = (event) => {
this.setState({ newItem: event.target.value });
}
render() {
const itenary = this.state.todoItems;
return (
<div>
{itenary.map(todo =>
<div key={todo.id}>
<Todo handleClick={this.props.handleClick} thing={todo} />
</div>
)}
<br />
<form onSubmit={this.addItem}>
<input type="text" onChange={this.handleInput} placeholder="Add a new task" />
<button>Submit</button>
</form>
</div>
);
}
}
export default TodoList;
Todo.js
import React from 'react';
class Todo extends React.Component {
constructor() {
super();
this.state = {
clicked: false
}
}
handleClick = () => {
this.setState({ clicked: !this.state.clicked });
}
render() {
const styles = this.state.clicked ? { textDecoration: 'line-through' } : { textDecoration: 'none' };
return (
{/* This is where the todo item is*/}
<div style={styles} onClick={this.handleClick} key={this.props.thing.id}>{this.props.thing.text}</div>
);
}
}
export default Todo;
todos.js
const todos = [
{ id: 1, text: 'Go to the gym', 'completed': false },
{ id: 2, text: 'Do laundry', 'completed': false },
{ id: 3, text: 'Study for exams', 'completed': false },
{ id: 4, text: 'Read a book', 'completed': false },
{ id: 5, text: 'Clean the bedroom', 'completed': false },
{ id: 6, text: 'Go to the park', 'completed': false },
];
export default todos;
Any help and/or feedback is appreciated.
You must give the new todo you add to todoItems a unique id that React can use to distinguish it from the others when you render them.
You should also not mutate the current state by using push. You should instead set state with an entirely new array that contains everything the previous one did.
Example
class TodoList extends React.Component {
constructor() {
super();
this.state = {
todoItems: todos,
newItem: ""
};
}
addItem = event => {
event.preventDefault();
this.setState(prevState => {
return {
todoItems: [
...prevState.todoItems,
{ id: Math.random(), text: prevState.newItem, completed: false }
],
newItem: ""
};
});
};
// ...
}
const todos = [
{ id: 1, text: "Go to the gym", completed: false },
{ id: 2, text: "Do laundry", completed: false },
{ id: 3, text: "Study for exams", completed: false },
{ id: 4, text: "Read a book", completed: false },
{ id: 5, text: "Clean the bedroom", completed: false },
{ id: 6, text: "Go to the park", completed: false }
];
class TodoList extends React.Component {
constructor() {
super();
this.state = {
todoItems: todos,
newItem: ""
};
}
addItem = event => {
event.preventDefault();
this.setState(prevState => {
return {
todoItems: [
...prevState.todoItems,
{ id: Math.random(), text: prevState.newItem, completed: false }
],
newItem: ""
};
});
};
handleInput = event => {
this.setState({ newItem: event.target.value });
};
render() {
const itenary = this.state.todoItems;
return (
<div>
{itenary.map(todo => (
<div key={todo.id}>
<Todo handleClick={this.props.handleClick} thing={todo} />
</div>
))}
<br />
<form onSubmit={this.addItem}>
<input
type="text"
onChange={this.handleInput}
value={this.state.newItem}
placeholder="Add a new task"
/>
<button>Submit</button>
</form>
</div>
);
}
}
class Todo extends React.Component {
constructor() {
super();
this.state = {
clicked: false
};
}
handleClick = () => {
this.setState({ clicked: !this.state.clicked });
};
render() {
const styles = this.state.clicked
? { textDecoration: "line-through" }
: { textDecoration: "none" };
return (
<div style={styles} onClick={this.handleClick} key={this.props.thing.id}>
{this.props.thing.text}
</div>
);
}
}
ReactDOM.render(<TodoList />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
You can always define the Object/s array within a function accepting an array:
In this example:
const [programs, setPrograms] = useState([]);
In order to create a single JSON program array:
setPrograms([{id: program?.id, title: program?.title}]);
How to create Array in JSON.
[ { "id": "1", "text": "Hello", "status": "false" }, { "id": "2", "text": "Coding Techniques", "status": "true" }, ]
Watch this video for more help
https://youtu.be/zgFOIdBIn4w
I'm brand new to Reactjs and I'm working on my first app, a todo app of course. However, everything was going smoothly until I was asked to create a delete button to remove my todos. I'm stuck and have been puzzled over this for almost 3 days. Any help or advice would be appreciated.
react-to-do/src/App.js
import React, { Component } from "react";
import "./App.css";
import ToDo from "./components/ToDo.js";
class App extends Component {
constructor(props) {
super(props);
this.state = {
todos: [
{ description: "Walk the cat", isCompleted: true },
{ description: "Throw the dishes away", isCompleted: false },
{ description: "Buy new dishes", isCompleted: false }
],
newTodoDescription: ""
};
this.deleteTodo = this.deleteTodo.bind(this);
}
deleteTodo(description) {
this.setState({
todos: this.state.todos.filter(
(todos, index) => todos.description !== description
)
});
}
handleChange(e) {
this.setState({ newTodoDescription: e.target.value });
}
handleSubmit(e) {
e.preventDefault();
if (!this.state.newTodoDescription) {
return;
}
const newTodo = {
description: this.state.newTodoDescription,
isCompleted: false
};
this.setState({
todos: [...this.state.todos, newTodo],
newTodoDescription: ""
});
}
toggleComplete(index) {
const todos = this.state.todos.slice();
const todo = todos[index];
todo.isCompleted = todo.isCompleted ? false : true;
this.setState({ todos: todos });
}
render() {
return (
<div className="App">
<ul>
{this.state.todos.map((todo, index) => (
<ToDo
key={index}
description={todo.description}
isCompleted={todo.isCompleted}
toggleComplete={() => this.toggleComplete(index)}
/>
))}
</ul>
<form onSubmit={e => this.handleSubmit(e)}>
<input
type="text"
value={this.state.newTodoDescription}
onChange={e => this.handleChange(e)}
/>
<input type="submit" />
</form>
</div>
);
}
}
export default App;
react-to-do/src/ToDo.js
import React, { Component } from "react";
class ToDo extends Component {
deleteToDo(description) {
this.props.deleteToDo(description);
}
render() {
return (
<div className="wrapper">
<button
className="deleteToDo"
onClick={e => this.deleteToDo(this.props.description)}
>
Delete
</button>
{this.props.deleteToDo}
<li>
<input
type="checkbox"
checked={this.props.isCompleted}
onChange={this.props.toggleComplete}
/>
<span>{this.props.description}</span>
</li>
</div>
);
}
}
export default ToDo;
react-to-do/src/index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import registerServiceWorker from "./registerServiceWorker";
ReactDOM.render(<App />, document.getElementById("root"));
registerServiceWorker();
Your code should look like
class ToDo extends React.Component {
deleteToDo(description) {
this.props.deleteToDo(description);
}
render() {
return (
<div className="wrapper">
<button className="deleteToDo" onClick = {(e) =>
this.deleteToDo(this.props.description)}>Delete</button>
{() => this.props.deleteToDo(this.props.description)}
<li>
<input type="checkbox" checked={ this.props.isCompleted }
onChange={ this.props.toggleComplete } />
<span>{ this.props.description }</span>
</li>
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
todos: [
{ description: 'Walk the cat', isCompleted: true },
{ description: 'Throw the dishes away', isCompleted: false },
{ description: 'Buy new dishes', isCompleted: false }
],
newTodoDescription: ''
};
this.deleteTodo = this.deleteTodo.bind(this);
}
deleteTodo(description) {
const filteredTodos = this.state.todos.filter((todo, index) => todo.description !== description);
this.setState({
todos: filteredTodos
});
}
handleChange(e) {
this.setState({ newTodoDescription: e.target.value })
}
handleSubmit(e) {
e.preventDefault();
if (!this.state.newTodoDescription) { return }
const newTodo = { description: this.state.newTodoDescription,
isCompleted: false};
this.setState({ todos: [...this.state.todos, newTodo],
newTodoDescription: '' });
}
toggleComplete(index) {
const todos = this.state.todos.slice();
const todo = todos[index];
todo.isCompleted = todo.isCompleted ? false: true;
this.setState({ todos: todos });
}
render() {
return (
<div className="App">
<ul>
{ this.state.todos.map( (todo, index) =>
<ToDo key={ index } description={ todo.description }
isCompleted={ todo.isCompleted } toggleComplete={ () =>
this.toggleComplete(index) } deleteToDo={this.deleteTodo} />
)}
</ul>
<form onSubmit={ (e) => this.handleSubmit(e) }>
<input type="text" value={
this.state.newTodoDescription } onChange={ (e) =>
this.handleChange(e) } />
<input type="submit" />
</form>
</div>
);
}
}
Here you forgot to pass props from App and description parameter from ToDo component.
Try here https://jsfiddle.net/prakashk/69z2wepo/101369/#&togetherjs=B3l5GDzo8A
You never pass your "deleteTodo" function as a prop to your "toDo" component. change the portion where you create you're todo components in that map to resemble something like this...
map.... => <toDo deleteToDo={this.deleteTodo}......... />
remember components are scoped to themselves if you want to call a function that exists in the parents context in a child, a reference of that function must be passed to that child as a prop.