I'm new to React and I'm trying create a To Do List project.
I'm trying to add a new task to my tasks's array via input, but when I press Enter nothing is added to screen. Can someone help?
App.js
import React, { Component } from "react";
import Tasks from "./Components/tasks";
class App extends Component {
constructor(props) {
super(props);
this.state = {
newTask: '',
tasks: [
{ id: 1, text: "study" },
{ id: 2, text: "read" },
{ id: 3, text: "gym" },
]
};
}
handleSubmit(e) {
e.preventDefault();
const tasks = [...this.state.tasks];
tasks.push({id: 4, text: this.state.newTask});
this.setState({ tasks: tasks });
}
handleChange= (e) => {
this.setState({newTask: e.target.value});
}
render() {
return (
<div className="App">
<h1>To Do List</h1>
<form onSubmit={this.handleSubmit}>
<input type="text" placeholder="Enter task" value={this.state.newTask} onChange={this.handleChange}/>
</form>
<Tasks tasks={this.state.tasks} />
</div>
);
}
}
export default App;
Adicionaly I'm getting this error on the console:
error
you need to bind your function to the class
simple solution is to use arrow function syntax
handleSubmit = (e) => {
instead of
handleSubmit(e) {
there are other ways to do it as well..
you can read this article to understand more https://www.freecodecamp.org/news/this-is-why-we-need-to-bind-event-handlers-in-class-components-in-react-f7ea1a6f93eb/
Related
Hello peepz of the web,
I've ran into mysterious corridor, which's too dark for me to see what the hell I'm going.. would love someone's flashlight to shine the way.
I have created a simple, poor to do list program.
It's combined from Task.js, TaskList.js and NewTaskForm.js.
From the NewTaskForm.js I'm retrieving input, passing it to the parent, TaskList.js.
On TaskList.js I'm adding a key to the task object:
handleSubmit(task2add){
let keyid = v4();
console.log(keyid);
let task2addWithID = { ...task2add, id: {keyid}, key: {keyid}};
this.setState(st => ({
todoArr: [...st.todoArr, task2addWithID],
}));
}
Now, in TaskList.js, in my render function, I'm creating Tasks:
render() {
let tasklist = this.state.todoArr.map(task => (
<div>
<Task
taskTitle={task.title}
taskText={task.text}
key={task.key}
id={task.id}
handleRemove={this.handleRemove}
handleEdit={this.handleEdit}
/>
</div>
));
return (
<div>
<h1>TaskList</h1>
<div className='TaskList'>
<div className='TaskList-title'>
{tasklist}
</div>
<NewTaskForm key={1} handleSubmit={this.handleSubmit}/>
</div>
</div>
)
}
now, what is so confusing for me is why when I'm doing in my Tasks.js class:
console.log(this.props.id);
it prints me an object?
I would expect it to.. print me a value? where along the way did it wrap it with an object?
Full (shitty) code below.
Plus #1, if anyone knows to tell me why still I get the warning the key, even though, at least for my poor experience, I have given it a key?
Plus #2, why even when I send the handleRemove function in TaskList.js the proper id, it doesn't erase the bloody task? :-(
Regards!
Task.js
import React, { Component } from 'react'
import './Task.css';
export default class Task extends Component {
constructor(props){
super(props);
this.handleRemove = this.handleRemove.bind(this);
}
handleRemove(evt){
evt.preventDefault();
console.log("MY ID MANNN");
console.log(this.props.id);
this.props.handleRemove(this.props.id.keyid);
console.log("CALLED");
}
render() {
return (
<div className='Task'>
<div className='Task-title'>
{this.props.taskTitle}
</div>
<div className='Task-text'>
{this.props.taskText}
</div>
<div>
<button className='Task-buttons'>Edit</button>
<button className='Task-buttons' onClick={this.handleRemove}>Delete</button>
</div>
</div>
)
}
}
NewTaskForm.js:
import React, { Component } from 'react';
import {v4} from 'uuid';
export default class NewTaskForm extends Component {
constructor(props){
super(props);
this.state = {
title: "", text: "", editing: false
}
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
// this.handleRemove = this.handleRemove.bind(this);
}
handleSubmit(evt){
evt.preventDefault();
// let stateWithID = { ...this.state, id: useId()};
this.props.handleSubmit(this.state);
this.setState({
title: "",
text: ""
})
}
handleChange(evt){
evt.preventDefault();
this.setState({
[evt.target.name]: evt.target.value
});
}
render() {
return (
<div>
<h2>Insert new Task:</h2>
<form onSubmit={this.handleSubmit}>
<label htmlFor="title">Title</label>
<input
name='title'
id='title'
type='text'
onChange={this.handleChange}
value={this.state.title}
/>
<div>
<label htmlFor="text">text</label>
<input
name='text'
id='text'
type='text'
onChange={this.handleChange}
value={this.state.text}
/>
</div>
<button>Submit</button>
</form>
</div>
)
}
}
TaskList.js
import React, { Component } from 'react'
import NewTaskForm from './NewTaskForm';
import Task from './Task';
import './TaskList.css';
import {v4} from 'uuid';
export default class TaskList extends Component {
constructor(props){
super(props);
this.state = {
todoArr: [
// {title: "test", text: "this shit", key: "", id: ""},
// {title: "test2", text: "this shi2", key: "", id: ""},
// {title: "test3", text: "this shi3", key: "", id: ""}
],
isEditing: false,
}
this.handleSubmit = this.handleSubmit.bind(this);
this.handleRemove = this.handleRemove.bind(this);
this.handleEdit = this.handleEdit.bind(this);
}
handleSubmit(task2add){
let keyid = v4();
console.log(keyid);
let task2addWithID = { ...task2add, id: {keyid}, key: {keyid}};
this.setState(st => ({
todoArr: [...st.todoArr, task2addWithID],
}));
}
handleRemove(keyid){
console.log("IN HANDLE REMOVE");
console.log(keyid);
this.setState(st => ({
todoArr: st.todoArr.filter(n => n.keyid !== keyid )
}));
}
handleEdit(id){
}
render() {
let tasklist = this.state.todoArr.map(task => (
<div>
<Task
taskTitle={task.title}
taskText={task.text}
key={task.key}
id={task.id}
handleRemove={this.handleRemove}
handleEdit={this.handleEdit}
/>
</div>
));
return (
<div>
<h1>TaskList</h1>
<div className='TaskList'>
<div className='TaskList-title'>
{tasklist}
</div>
<NewTaskForm key={1} handleSubmit={this.handleSubmit}/>
</div>
</div>
)
}
}
Because you have created an object here:
{ ...task2add, id: {keyid}, key: {keyid}};
{keyid}
is the same as
{keyid: keyid}
You wanted to do this:
{ ...task2add, id: keyid, key: keyid};
I'm assuming its because you're setting the state like this:
let task2addWithID = { ...task2add, id: {keyid}, key: {keyid}};
The id attribute is assigned an object. If you did:
let task2addWithID = { ...task2add, id: keyid, key: {keyid}};
console logging id should print out a string
I'm truing to make a todo-app for practicing. For some reason when I press the add button nothing happen, but then if I apply "onChange" on the input field the created todo does display in the todos list. I've been trying to find a solution for that for the last few hours, I believe I missed something... hope someone could figure it out !
App.js
class App extends Component {
state = {
newTask: { id: 0, task: "" },
tasks: [
{ id: 1, task: "task1" },
{ id: 2, task: "task2" },
{ id: 3, task: "task3" }
]
};
newTask = e => {
this.setState({
newTask: { id: this.state.tasks.length + 1, task: e.target.value }
});
};
addTask = e => {
this.setState(prevState => {
prevState.tasks.push(this.state.newTask);
});
};
render() {
return (
<div className="App">
<Header title="Todo List" />
<Form input={this.newTask} add={this.addTask} />
<ul>
{this.state.tasks.map(t => (
<Tasks task={t.task} key={t.id} />
))}
</ul>
</div>
);
}
}
export default App;
const form = props => {
return (
<div>
<input
type="text"
placeholder="Type your task here..."
onChange={props.input}
/>
<button onClick={props.add}>Add</button>
</div>
);
};
const tasks = props => {
return <div>{props.task}</div>;
};
You doesn't return anything inside setState function in addTask. Therefore you actually update state, but do not trigger react update lifecycle.
I suppose you to try smth like this:
addTask = e => {
this.setState(prevState => {
return { tasks: [...prevState.tasks, this.state.newTask]};
});
};
I have functional component below:-
import React from 'react'
import { Dropdown } from 'semantic-ui-react'
const DropDownMenu= (props)=> {
const options = [
{ key: 'fruits', text: 'fruits', value: 'Fruits' },
{ key: 'vegetables', text: 'vegetables', value: 'Vegetables' },
{ key: 'home-cooked', text: 'home-cooked', value: 'Home-Cooked' },
{ key: 'green-waste', text: 'green-waste', value: 'Green-Waste' },
{ key: 'other', text: 'other', value: 'other' },
];
function onChangeHandler(e) {
console.log(e.target.innerText);
props.getCategoryValue(e.target.innerText);
};
return (
<Dropdown placeholder='Category' fluid selection options={options}
onChange={onChangeHandler} />
);
};
export default React.memo(DropDownMenu);
Above functional component is being rendered in its parent component sellForm.js as below:-
import React,{Component} from 'react'
import { Button, Form} from 'semantic-ui-react'
import AutoCompleteInput from '../GoogleAutocomplete/autoComplete';
import DropDownMenu from '../DropDown/DropDown';
import update from 'react-addons-update';
import './sellForm.css';
import PreviewImages from '../PreviewImage/previewUploadedImages';
import FileInput from '../FileInput/FileInput';
class sellForm extends Component{
constructor(props){
super(props);
//this.imageUpload = React.createRef();
this.state={
postID: '',
title: '',
description:'',
category:'',
price: '',
amount: '',
freshness: '',
contact: '',
location: '',
timestamp: '',
images: []
}
}
getCategoryValue=(category)=>{
this.setState({category: category})
};
getItemLocation=(locationObject)=>{
this.setState({location: locationObject})
};
saveInfo=(e)=>{
this.setState({
[e.target.name]:e.target.value});
};
postButtonClickHandler=()=>{
console.log(this.state)
console.log(typeof (this.state.images[0].file))
// send this info to firebase database
};
handleImageUpload= (file)=>{
console.log('handle image Upload in sell form');
this.setState({
images: update(this.state.images, {$push: [file]})
})
};
handleImageDeletion=(indexOfImage)=>{
console.log('handle image deletion in sell form - index to be deleted is : ' ,indexOfImage);
this.setState((prevState)=>{
return{
// images: prevState.images.splice(indexOfImage,1)
images: update(this.state.images, {$splice: [[indexOfImage,1]]})
}
})
};
shouldComponentUpdate(nextProps,nextState){
console.log('[sellform.js] shouldComponentUpdate');
return true;
}
componentDidMount(){
console.log('[sellform.js] componentDidMount');
}
static getDerivedStateFromProps(props, state){
//when user uploads or deletes images, then props changes
//this lifecycle executes when function gets new props before render()
//only use when component's inner state depends upon props...
console.log('[sellform.js] getDerivedStateFromProps')
return null;
}
componentDidUpdate(prevProps){
console.log('[sellform.js] componentDidUpdate')
}
componentWillUnmount(){
console.log('[sellform.js] componentWillUmMount')
}
render(){
console.log('render of sellForm');
console.log(this.state.images);
let previewImages = (<PreviewImages deleteUploadedImage={this.handleImageDeletion} images={this.state.images}/>)
return(
<Form>
<Form.Field>
<DropDownMenu getCategoryValue={this.getCategoryValue}/>
</Form.Field>
<Form.Field>
{<AutoCompleteInput
onChange={()=>{}}
onPlaceSelected={this.getItemLocation}/>}
</Form.Field>
<Form.Field>
<input
placeholder='What are you selling ?'
name="title"
onChange={this.saveInfo}/>
</Form.Field>
<Form.Field>
<input
placeholder='Price'
name="price"
onChange={this.saveInfo} />
</Form.Field>
<Form.Field>
<FileInput appendImageToArray={this.handleImageUpload}/>
</Form.Field>
<Form.Field>
<Button
type='submit'
onClick={this.postButtonClickHandler}>Post
</Button>
</Form.Field>
<Form.Field>
<div className='previewImageContainer'>
{previewImages}
</div>
</Form.Field>
</Form>
)
}
}
export default sellForm
when sellFom renders it gives following error:-
Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
Check the render method of sellForm.
at invariant (react-dom.development.js:57)
Any ideas react community ??
I solved this issue by updating both react and react-dom to 16.6.0.
Hey I think problem is due to naming od sellForm. Ax far as know, React accepts CamelCase Name for classes. Take this example for now:
function Example() {
// Declare a new state variable, which we'll call "count"
return (
<div>
<p>Tes</p>
</div>
);
}
const MemoizedExample = React.memo(Example)
function exampleParent() {
// Declare a new state variable, which we'll call "count"
return (
<div>
<p>Parent</p>
<MemoizedExample />
</div>
);
}
ReactDOM.render(<exampleParent />, document.getElementById("root"))
<script src="https://unpkg.com/react#16.7.0-alpha.0/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#16.7.0-alpha.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
In above I have made name as smallcase for class, see the rendering doesn't happen.
If you change the name of component from exampleComponent to ExampleComponent It will work. Similiarly for your problem change your class name from sellFrom to SellForm :). Here is the working one with component name camelcase:
function Example() {
// Declare a new state variable, which we'll call "count"
return (
<div>
<p>Tes</p>
</div>
);
}
const MemoizedExample = React.memo(Example)
function ExampleParent() {
// Declare a new state variable, which we'll call "count"
return (
<div>
<p>Parent</p>
<MemoizedExample />
</div>
);
}
ReactDOM.render(<ExampleParent />, document.getElementById("root"))
<script src="https://unpkg.com/react#16.7.0-alpha.0/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#16.7.0-alpha.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
I try to fill in a dropdown with data from the JSON format but for now the dropdown is empty (no results found...)
I certainly have a mistake and I can not understand where I'm confusing.
I will attach a screen of my API.
I want to get Station and NameStation..
API for Stations
My code:
import React, { Component } from 'react';
import Select from 'react-select';
import 'react-select/dist/react-select.css';
function parseStations(stations){
return stations.map((station) => {
return { label: station.NameStation, value: station.Station };
});
}
export default class Weather extends Component {
constructor(props) {
super(props);
this.state = {
options: [
{ value: true, label: 'Yes' },
{ value: false, label: 'No' }
], stations: [
],
value: null
}
this.onChange = this.onChange.bind(this);
}
onChange(event) {
this.setState({ value: event.value });
console.log('Boolean Select value changed to', event.value);
}
componentDidMount() {
this.getStations();
}
getStations() {
fetch('http://localhost:56348/api/stations', {
data: 'Station',
data: 'NameStation',
method: "GET"
}).then(res => res.json())
.then(res => this.setState({ stations: parseStations(res.stations) }))
//.then(res => this.setState({ stations: res.stations }))
//.catch(e => )
}
render() {
return (
<div className="MasterSection">
<div className="wrapper">
<div className="section">Изберете № на станция</div>
<Select
onChange={this.onChange}
//options={this.state.options}
options={this.state.stations}
value={this.state.value}
clearable={false}
/>
</div>
<div class="section">
<input type="text" class="form-control" placeholder="Брой дни назад" aria-label="Username" aria-describedby="basic-addon1"></input>
</div>
<div class="section">
<button type="button" class="btn btn-outline-dark">Покажи</button>
</div>
</div>
);
}
}
Seems you made a typo naming the prop stations instead of options :
<Select
onChange={this.onChange}
options={this.state.stations} // here
value={this.state.value}
clearable={false}
/>
Edit : you'll need to parse your json first to pass a proper array of objects like this : [{ label: nameStation, value: Station }]
Edit 2 : Here's a parser for your data :
function parseStations(stations){
return stations.map((station) => {
return { label: station.NameStation, value: station.Station };
});
}
You can call this in your async request before setting the state :
.then(res => this.setState({ stations: parseStations(res.stations) }))
componentDidMount() is executed only after render() is completed. so there's no way getStations() gets executed at the time your UI gets rendered. it is not a good idea to setState inside componentDidMount() as it triggers re rendering. use componentWillMount() instead.
correct the typo that Dyo mentioned and use options={this.state.stations}
I have a todolist in React, I can delete todo-s but I want to apply strike-through for completed todos. After that it would be great to list them as completed. How is it possible? What should I change in my code? I tried to use objects in the array, but that lead to diff, erros.
import React, { Component } from 'react';
class ToDoList extends Component {
constructor(props) {
super(props);
this.state = {
list: [],
items: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleRemove = this.handleRemove.bind(this);
}
handleChange(event) {
this.setState({items: event.target.value})
console.log(event.target.value);
}
handleSubmit(event) {
this.setState({
list: [...this.state.list, this.state.items],
items: ''
})
event.preventDefault();
}
handleRemove(index) {
const filteredArray = this.state.list.filter((_, i) => i !== index); // used underscore as a convention to address nothing is going there
this.setState({
list: filteredArray
});
}
render() {
return (
<div className='header main'>
<form onSubmit={this.handleSubmit} >
<label>
<input className='new-todo'
placeholder='What needs to be done?'
type="text"
value={this.state.items}
onChange={this.handleChange} />
</label>
</form>
<ul className='todo-list'>
{this.state.list.map((item, index) => (
<li className='list-view' key={index+1}>{item}<button className='list-view-button' onClick={this.handleRemove.bind(this, index) }>X</button></li>
))}
</ul>
<div className='footer'>
Remaining: {this.state.list.length}
</div>
</div>
);
}
}
export default ToDoList;
Well currently you only have an array of strings that represents the todos.
I would do this for your items state:
items: [
{
desc: "todo content",
status: "new"
},
{
desc: "todo content",
status: "completed"
},
{
desc: "todo content",
status: "archived"
}
];
now when you loop through the todos you can check for the status for different design display.
Or you can filter the todos, for specific status,
ie:
this.state.items.filter(item => item.status==="new")
this will give you only the "new" todos.