I need some help building a todo list with React. Im a beginner so there are a lot of aspects of react I don't understand. I've created a Todo list at the moment this lists an un ordered list with the input, edit and remove button a select menu and a further input field.
Firstly Im confused to where I update my state. the "App.js" is where my main state is stored and im trying to keep it that way. I need to know how to edit the input field (todo listitem) which stores the new value. Im then looking to create a "completed list" where i want to store the input field as well as the select option (which ever is clicked) Please could someone give me some guidelines for this. Thank you in advance
import React, { Component } from 'react';
import Form from './Components/Form'
import './App.css';
import List from './Components/List'
import Completed from './Components/Completed'
class App extends Component {
constructor(props){
super(props)
this.state={
isEditing:false,
text:"",
items:[],
completed:[
{
}
]
}
this.submit=this.submit.bind(this);
this.eventHandler=this.eventHandler.bind(this)
}
submit=(e)=>{
e.preventDefault();
this.setState({
items:[
{
name:this.state.text,
},
...this.state.items
],
text:""
})
}
remove=(index)=>{
this.setState({
items:this.state.items.filter((_,i) => i!==index)
})
}
onChange=(index)=>{
this.setState({
items:this.state.items.filter((_,i) => i!==index)
});
}
eventHandler=(e)=>{
this.setState ({
text:e.target.value
})
}
handleNameEdits=()=>{
this.setState({
isEditing:true
})
}
edit=()=>{
this.setState({
isEditing:!this.state.isEditing
})
}
myoptions=(e)=>{
this.setState({
completed:[
{
webmaster:e
},
...this.state.completed
]
})
}
render() {
return (
<div className="App">
<header className="App-header">
<Form submit={this.submit} myValue={this.state.text} eventHandler=
{this.eventHandler}/>
{this.state.items.map && this.state.items.map((item,index)=>(
<List key={index}
name={item.name}
edit={this.edit}
change={()=>this.onChange(index)}
remove={()=>this.remove(index) }
handleNameEdits={this.handleNameEdits}
myoptions={(e =>this.myoptions(e.target.value))}
/>
))}
</header>
<div>
completed
</div>
</div>
);
}
}
export default App;
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Edit from './Edit';
class List extends Component {
constructor(props) {
super()
this.options = [
{name:'web1'},
{name:'web2'},
{name:'web3'},
{name:'web4'}
];
}
render() {
const {key} = this.props;
const x=this.options;
return (
<ul>
<li>{this.props.name}
<button onClick={this.props.edit}>Edit</button>
<button onClick={this.props.remove}>Remove</button>
<select onChange={this.props.myoptions}>
{this.options.map(options => <option>{options.name}</option> )}
</select>
<label> Completed
</label><input type="checkbox" onChange=
{this.props.change} checked={this.props.change} onClick=
{this.props.submit}/>
<label> Ticket Number </label><input type='text'/>
</li>
</ul>
)
}
}
export default List;
import React from 'react'
import PropTypes from 'prop-types';
const Form= props=> {
return (
<form onSubmit={props.submit}>
<input type='text' value={props.myValue} onChange=
{props.eventHandler}/>
<button> click</button>
</form>
)
}
Form.PropTypes={
onSubmit:PropTypes.func.isRequired,
evenHandler:PropTypes.func.isRequired,
myValue:PropTypes.string.isRequired
}
export default Form
Related
I am building a To-Do List web app with React as my first project.
I want to implement local storage which works fine only that,I am unable to handle check and uncheck of the checkbox prefectly.
Here is a link to the deployed website so you can understand the problem I am having.
https://rapture-todo.netlify.app/
When you add a todo, and mark it complete.
on reload, the checkbox of the todo is unchecked but the todo is marked complete.
Here is my source code[github link- https://github.com/coolpythoncodes/React-ToDo-List].
For App.js
import React, { Component } from 'react';
import Header from './component/Header';
import Info from './component/Info';
import AddToDo from './component/AddToDo';
import TodoListItem from './component/TodoListItem';
import './sass/main.scss';
class App extends Component{
constructor(props){
super(props);
this.state= {
value: '',
list: [],
show: true,
};
this.handleChange= this.handleChange.bind(this);
this.handleSubmit= this.handleSubmit.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
this.deleteTask = this.deleteTask.bind(this);
}
componentDidMount() {
const list = window.localStorage.getItem('userTodo') ? JSON.parse(localStorage.getItem('userTodo')) : [];
this.setState({ list })
}
handleChange(e) {
this.setState({value:e.target.value})
}
// Handle submission of user todo item
handleSubmit(e) {
e.preventDefault();
const newTask = {
id: Date.now(),
userTodo: this.state.value,
isCompleted: false,
checked: false,
}
// Validate form so user doesn't add an empty to do
if (this.state.value.length > 0) {
this.setState({
list: [newTask, ...this.state.list],
value: '', // Clear input field
show: true, // Success message
}, ()=>{
window.localStorage.setItem('userTodo', JSON.stringify(this.state.list));
})
}
}
// Handles checkbox
handleInputChange(id) {
this.setState({list: this.state.list.map(item => {
if (item.id === id) {
item.isCompleted = !item.isCompleted;
item.checked = !this.state.checked;
}return item
})}, ()=>{
window.localStorage.setItem('userTodo', JSON.stringify(this.state.list));
})
}
// Delete a task
deleteTask(id){
this.setState({list: this.state.list.filter(item => item.id !== id )},()=>{
window.localStorage.setItem('userTodo', JSON.stringify(this.state.list))
})
console.log(this.state.list)
}
render(){
return(
<div>
<Header />
<Info />
<AddToDo onChange={this.handleChange} value={this.state.value} onSubmit={this.handleSubmit} />
<TodoListItem deleteTask={this.deleteTask} onChange={this.handleInputChange} list={this.state.list} defaultChecked={this.state.checked} />
</div>
)
}
}
export default App;
For TodoListItem.js
import React, { Component } from 'react';
import ToDoItem from './ToDoItem';
import '../sass/main.scss';
class ToDoListItem extends Component{
render(){
const {list, onChange, deleteTask, defaultChecked} = this.props;
return(
<div>
{list.map((todo)=>{
return (
<ToDoItem
key={todo.id}
userTodo={todo.userTodo}
isCompleted={todo.isCompleted}
onChange={onChange}
id={todo.id}
deleteTask={deleteTask}
defaultChecked={defaultChecked}
/>
)
})}
</div>
)
}
}
export default ToDoListItem;
For TodoItem.js
import React, { Component } from 'react';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faTrashAlt } from '#fortawesome/free-solid-svg-icons'
import '../sass/main.scss';
class ToDoItem extends Component{
render(){
const {userTodo, isCompleted, onChange, id, deleteTask, defaultChecked} = this.props;
const checkStyle = isCompleted ? 'completed-todo' : 'not-completed-todo';
return(
<div className={`container ${checkStyle}`}>
<input type="checkbox" onChange={onChange.bind(this, id)} defaultChecked={defaultChecked}/>
<div >
<p className='title'>{userTodo}</p>
</div>
{/* Delete button */}
<button onClick={deleteTask.bind(this, id)}><FontAwesomeIcon className='remove-icon' icon={faTrashAlt} /></button>
</div>
)
}
}
export default ToDoItem;
Please note: I have gone through other questions similar to the problem I am having but I could not solve this problem.
If I did not state the question well, please let me know.
In the below code in App.js,
<TodoListItem deleteTask={this.deleteTask} onChange={this.handleInputChange} list={this.state.list} defaultChecked={this.state.checked} />
You are setting, defaultChecked={this.state.checked} Why do you do that? There is nothing called checked in the state.
In fact, there is no need to pass the defaultValue.
Make the following changes,
In App.js, remove defaultValue prop for TodoListItem
<TodoListItem deleteTask={this.deleteTask} onChange={this.handleInputChange} list={this.state.list}/>
In TodoListItem.js, remove defaultChecked={defaultChecked}
<ToDoItem
key={todo.id}
userTodo={todo.userTodo}
isCompleted={todo.isCompleted}
onChange={onChange}
id={todo.id}
deleteTask={deleteTask}
defaultChecked={defaultChecked} // Remove this.
/>
In ToDoItem.js,
<input type="checkbox"onChange={onChange.bind(this, id)}
defaultChecked={isCompleted} // Replace defaultValue with isCompleted
/>
Im trying to simply create a search and results feature for an app. The value of the input should reflect the components listed in the CardList Array. The filter doesn't seem to update the CardList. I've logged steps along the way and I've come to the conclusion that its the filter I set up. I cant seem to figure out why it wont filter the list.
import React, {Component} from 'react';
import CardList from './CardList';
import {robots} from './robots';
import './index.css';
class App extends Component {
constructor() {
super()
this.state = {
robots: robots,
searchfield: ''
}
}
onSearchChange = (event) => {
this.setState({ searchfield: event.target.value });
}
render() {
const filteredRobots = this.state.robots.filter(robot => {
return robot.name.toLowerCase().includes(this.state.searchfield.toLowerCase());
});
return (
<div className="appAlign">
<h1 className="appTitle">RoboFriends</h1>
<input
className="searchBox"
type="search"
placeholder="Search Robots"
onChange={this.onSearchChange}
/>
<CardList robots={filteredRobots} />
</div>
);
}
}
export default App;
The error is not caused by the filter function as I have tested it and it works. It most probably lies with the robots data-set. I have slightly modified the filter function here.
import React, { Component } from "react";
import CardList from "./CardList";
import { robots } from "./robots";
class App extends Component {
constructor() {
super();
this.state = {
robots: robots,
searchfield: ""
};
}
onSearchChange = event => {
this.setState({ searchfield: event.target.value });
};
render() {
const filteredRobots = this.state.robots.filter(robot =>
robot.name.toLowerCase().includes(this.state.searchfield.toLowerCase())
);
return (
<div className="appAlign">
<h1 className="appTitle">RoboFriends</h1>
<input
className="searchBox"
type="search"
placeholder="Search Robots"
onChange={this.onSearchChange}
/>
<CardList robots={filteredRobots} />
</div>
);
}
}
export default App;
I have made a sandbox with your code which has a sample robots data and a Card that renders the filtered data-set. Take a look.
I am writing a react application that outputs a list of books (image, title, category, and description).
My search bar and booklist are sibling components and the search bar will pass data to the booklist.
when clicking the search button, only "Sample Category" shows up but not anything else. There is no problem accessing the API and the data is not null.
Here is a sample API output: https://www.googleapis.com/books/v1/volumes?q=lordoftherings
My code is the following:
// App
import React, { Component } from 'react';
import Axios from 'axios';
import './App.css';
import SearchBar from './SearchBar';
import BookList from './BookList';
class App extends Component {
constructor(props) {
super(props);
this.state = {
books: []
};
this.search = this.search.bind(this);
}
search(title) {
const promise = Axios.get('https://www.googleapis.com/books/v1/volumes?q=' + title);
promise.then((response) => {
const books = response.data.items;
this.setState({ books: books });
console.log(this.state.books);
})
};
render() {
return (
<div className="App">
<SearchBar searchBooks = {this.search}/>
<BookList booklist = {this.state.books}/>
</div>
);
}
}
export default App;
// Search Bar
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class SearchBar extends Component {
constructor(props) {
super(props);
this.state = { titleToSearch: 'harry potter' }
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(e) {
this.setState({ titleToSearch: e.target.value });
};
render() {
return (
<form>
<input
type="text"
name="booksInput"
placeholder="Enter book title"
value={this.state.titleToSearch}
onChange={this.handleInputChange}
/>
<button type="button" onClick={() => this.props.searchBooks(this.state.titleToSearch)}>Search</button>
</form>
);
}
}
export default SearchBar;
// BookList
import React, { Component } from 'react';
class BookList extends Component {
render() {
const books = this.props.booklist;
return (
<div className="table">
{books.map((book) => {
console.log(book.id);
return (
<div className="box" key={book.id}>
<div className="img"><img src="assets/default-placeholder.jpg" alt="" /></div>
<div className="title">{book.title}</div>
<div className="category">Sample Category</div>
<div className="description">{book.description}</div>
</div>
);
})}
</div>
);
}
}
export default BookList;
In the sample code you provided, you're not actually dynamically outputting categories.
You've hard coded 'Sample category' in there.
book.category
...is not actually in the dataset.
There are categories which seem to be available under:
<div className="category">{book.volumeInfo.categories[0]}</div>
although you'll want to check if the array has length, and probably map or join each item in array to string.
just to be clear: the issue with your other fields is also that they're children of "volumeInfo"
I intend to create a to-do list without using ref as in the many examples, but it isn't working.
The expected behavior is that upon entering an entry, it will show up at the top and upon clicking add, it will create an input box for entering an entry. Currently, upon entering the state returns undefined.
The code can be found below or in this sandbox:
import React, { Component } from 'react';
import { render } from 'react-dom';
import './style.css';
import ToDo from './todo'
class App extends Component {
constructor() {
super();
this.state = {
todos: []
};
}
onChange=(e)=>{
const newToDos = [...this.state.todos]
newToDos.push(e.target.value)
this.setState({
todos: newToDos
})
}
onAdd=(e)=>{
e.preventDefault();
const newtodos=[...this.state.todos]
this.setState({
todos: newtodos.push("")
})
}
render() {
console.log(this.state.todos)
return (
<div>
<p>All the to-dos include {this.state.todos}</p>
<ToDo
todos={this.state.todos}
/>
<form onSubmit={this.onChange}>
<input
type="text"
placeholder="add a new todo..."
/>
</form>
<button onClick={this.onAdd}>+</button>
</div>
);
}
}
render(<App />, document.getElementById('root'));
And here is the todo.js:
import React, { Component } from 'react';
import { render } from 'react-dom';
export default class ToDo extends Component {
constructor(props){
super(props)
}
render() {
const {todos, onChange}=this.props
return (
<div>
{
todos.map((todo, index)=>
<div>
{todo}
</div>
)
}
</div>
);
}
}
You can store your new todo in state when onChange input and add this into todos when click save.
I have forked and edit your sample.
https://stackblitz.com/edit/react-nwtp5g?file=index.js
BTW: In your sample, newtodos.push("") will return the length of newtodos array, not the array after pushed.
onAdd=(e)=>{
e.preventDefault();
const newtodos=[...this.state.todos]
this.setState({
todos: newtodos.push("")
})
Hope this help.
your code newtodos.push("") dosent return array so no map function:
this.setState({
todos: newtodos.push("")
})
correct it something like this
this.setState({
todos: newtodos.concat("new value")
})
You have a problem with this code,
<form onSubmit={this.onChange}>
<input
type="text"
placeholder="add a new todo..."
/>
</form>
Here you are adding onSubmit on form, which will never call because you don't have submit button.
you should do something like this,
<form>
<input
type="text"
placeholder="add a new todo..."
onChange={this.onChange}
value={this.state.currentValue}
/>
</form>
onChange=(e)=>{
event.preventDefault();
this.setState({
currentValue: e.target.value
})
}
onAdd=(e)=>{
e.preventDefault();
const newToDos = [...this.state.todos]
newToDos.push(this.state.currentValue)
this.setState({
todos: newToDos,
currentValue: ''
})
}
Demo
Update
In your todo component you have useless constructor, If you don't have state in a component or don't have any function to bind this don't add constructor.
You can remove the constructor.
Another thing is, you are not passing any onChange prop to todo component, so here you will get undefined for onChange.
const {todos, onChange}=this.props
You can also write this component as a functional component.
You can update your code with
import React, { Component } from 'react';
import { render } from 'react-dom';
import './style.css';
import ToDo from './todo'
class App extends Component {
constructor() {
super();
this.state = {
todos: [],
inputText: ""
};
}
onAdd= () => {
this.setState({
todos: [...this.state.todos, this.state.inputText], textInput: ""
})
}
render() {
console.log(this.state.todos)
return (
<div>
<p>All the to-dos include {this.state.todos}</p>
<ToDo
todos={this.state.todos}
/>
<form>
<input
type="text"
placeholder="add a new todo..."
onChange={inputText => this.setState({inputText})}
/>
</form>
<button onClick={this.onAdd}>+</button>
</div>
);
}
}
render(<App />, document.getElementById('root'));
And in todo.js you can simply do
import React, { Component } from 'react';
import { render } from 'react-dom';
export default const ToDo = ({todos}) => {
return(<div>
{todos.map((todo, index) => (
<div key={index}>
{todo}
</div>))}
</div>)}
as it do not contains any state associated with it.
I am following this tutorial React Tutorial
Here is the thing:
I am calling function createTask from create-todo.js;
the function is defined in app.js
app.js
import React from 'react';
import ToDosList from './todos-list';
import CreateToDoItem from './create-todo-item'
const todoitems=[
{
task: 'finish react todo tutorial',
isCompleted: false
},
{
task: 'eat lunch',
isCompleted: true
}
];
export default class App extends React.Component
{
constructor(props){
super(props)
this.state={
todoitems
};
}
render()
{
return(
<div>
<h1>React Demo App - ToDos </h1>
<CreateToDoItem createtask ={this.createTask.bind(this)}/>
<ToDosList
todoitems={this.state.todoitems}
/>
</div>
);
}
createTask(task)
{
//alert('called');
this.state.todoitems.push({
task,
isCompleted:false
});
this.setState({todoitems: this.state.todoitems});
}
}
create-todo.js
import React from 'react';
import App from './app';
export default class CreateToDoItem extends React.Component
{
render()
{
return(
<form onSubmit={this.handleCreate.bind(this)}>
<input type="text" placeholder="what do I need to do?" ref="createInput"/>
<button>Create</button>
</form>
);
}
handleCreate(event)
{
event.preventDefault();
//alert('called');
this.props.createTask(this.refs.createInput.value); //this throws error
}
}
I am absolutely new to React.js. I don't know how this works. Should the function be available to create-todo.js? The code is exactly how it is shown in the tutorial.
Reason is, you are passing the function in props by a different key and using inside child by a different key.
Actual function name: createTask
Passing in props by key: createtask
Using inside child by: createTask
so use this.props.createtask() instead of this.props.createTask()
Your prop is named "createtask". You should probably change your prop name to createTask (as opposed to changing references to this.props.createtask).
You could do like this:
import React from 'react';
import ToDosList from './todos-list';
import CreateToDoItem from './create-todo-item'
const items = [
{
task: 'finish react todo tutorial',
isCompleted: false
},
{
task: 'eat lunch',
isCompleted: true
}
];
export default class App extends React.Component {
state = {
todoitems: items,
taskItem: ''
}
handleChange = (e) => {
this.setState({
taskItem: e.target.value
})
}
handleCreate = () => {
e.preventDefault()
item = {
task: taskItem,
isCompleted: false // default value
}
this.setState({
todoitems: [...this.state.todoitems, item ]
})
}
render() {
return (
<div>
<h1>React Demo App - ToDos </h1>
<CreateToDoItem
handleChange={this.handleChange}
handleCreate={this.handleCreate}
taskItem={this.state.taskItem}
/>
<ToDosList todoitems={this.state.todoitems}
/>
</div>
);
}
}
Then your form will be:
import React from 'react';
import App from './app';
export const CreateToDoItem = (props) => (
<form onSubmit={props.handleCreate}>
<input type="text" placeholder="what do I need to do?"
onChange={props.handleChange}
value={props.taskItem} />
<button type='submit'>Create</button>
</form>
);
I hope it help you :)
Kindly,