Building a Simple Todo List App in React

Ok… Here it is… :slight_smile:

TodoItems.js:

import React, { Component } from "react";
 
class TodoItems extends Component {

  constructor(props) {
    super(props);
    this.delete = this.delete.bind(this);
  }

  delete(key) {
    this.props.delete(key);
  }

  createTasks(item) {
    return <li onClick={() => this.delete(item.key)} 
    key={item.key}>{item.text}</li>
  }
 
  render() {
    var todoEntries = this.props.entries;
    var listItems = todoEntries.map(this.createTasks);
 
    return (
      <ul className="theList">
          {listItems}
      </ul>
    );
  }
};
 
export default TodoItems;

TodoList.js

import React, { Component } from "react";
import TodoItems from "./TodoItems";
import "./TodoList.css";
 
class TodoList extends Component {
  constructor(props) {
    super(props);
    this.state = {
      items: []
    };
    this.addItem = this.addItem.bind(this);
    this.deleteItem = this.deleteItem.bind(this);
  }
  addItem(e) {
    if (this._inputElement.value !== "") {
      var newItem = {
        text: this._inputElement.value,
        key: Date.now()
      };
   
      this.setState((prevState) => {
        return { 
          items: prevState.items.concat(newItem) 
        };
      });
      this._inputElement.value = "";
    }
     
    console.log(this.state.items);
       
    e.preventDefault();
  }

  deleteItem(key) {
    var filteredItems = this.state.items.filter(function (item) {
      return (item.key !== key);
    });
   
    this.setState({
      items: filteredItems,
    });
  }

  render() {
    return (
      <div className="todoListMain">
        <div className="header">
          <form onSubmit={this.addItem}>
            <input ref={(a) => this._inputElement = a} placeholder="enter task">
            </input>
            <button type="submit">add</button>
          </form>
        </div>
        <TodoItems entries={this.state.items} delete={this.deleteItem}/>
      </div>
    );
  }
}
 
export default TodoList;

Hi Kirupa,
I think I found the reason for the error:
Under TodoItem.js, I should change this

  constructor(props) {
    super(props);
    this.delete = this.delete.bind(this);

to

constructor(props) {
super(props);
this.createTasks = this.createTasks.bind(this);

Thanks so much for looking into this :smile:

Your timing is scarily good haha. I was about to post the same thing. I have updated the tutorial to mention this. The version on Github also has the correct line of code: https://github.com/kirupa/kirupa/blob/master/reactjs/todolist/src/TodoItems.js

Can you share your code?

Nice tutorial @kirupa. Thanks a lot!

I have a question. Let’s say that onClick of li item, instead of deleting it, i would like to show the text in input, change it and when i hit add to update this specific item. How would i do this?

could you please explain the javascript syntax of the ref tag used on the input element of the TodoList component. thanks

ref={(a) => this._inputElement = a}

It is using an ES6 feature known as arrow functions that is a shorthand way of writing the following:

(function (a) {
  return this._inputElement = a;
});

The end result is that the referenced element is assigned to the _inputElement property.

The Mozilla documentation is a good place to learn more about it: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

I also recorded a short video in case that helps: https://www.youtube.com/watch?v=Sh-NYbDcUug

:slight_smile:

2 Likes

Do you have a demo page for the Todo List?

I can’t get mine to work. I get “items not defined”.

thank you

Here you go: https://www.kirupa.com/react/examples/todo.htm :slight_smile:

1 Like

Thanks a lot :slight_smile:

I’m still confused about the use of the “ref” property and the purpose of the callback.

ref={(a) => this._inputElement = a}

I understand that it equals the code below.

(function (a) {
return this._inputElement = a;
});

But, what’s the purpose of returning this.inputElement. What does “this” refer to and what is “a”?

Hi Kirupa,

I have another question for you :grinning:
This evening I completed the Todo List, and I think I understand most of it.
I’m trying build upon it, and make a wish list app.

It looks like this, for right now.

The only thing is I’m having a hard time figuring out how to get these in the list to display. I thought I could:

<input ref={(a) => this._inputElement = a}
   placeholder="item">
</input>
<input ref={(b) => this._inputElement = b}
   placeholder="description">
</input>
<input ref={(c) => this._inputElement = c}
    placeholder="link">
</input>

But nothing is happening.

I know you’re probably busy, but if you have a chance, could help point me where in the code I could get this to work?

@Elizabeth_Amanda,

Generally you want to avoid using ref if you can. The problem with your example is that you have 3 inputs and each one has a ref which assigns itself to the same instance property, _inputElement. So instead of having references to 3 different inputs, you have one reference that gets assigned a different input reference 3 different times - whose value will be the last input reference assigned to it. In fact it looks like you tried to fix this by changing the reference variable, but that’s not the one that makes a difference (a, b, and c). That can be the same for each, its the _inputElement that you’d want to be different.

But again, using ref isn’t preferred and can be avoided. So instead, set input values to state properties and detect changes with onChange. You just need to make sure you have properties for each of your inputs. Here’s an example: https://jsfiddle.net/v4qvt8bk/

thank you @senocular, I definitely have a lot more to learn!

That’s a good question :stuck_out_tongue:

The easiest approach would be to set the contenteditable attribute on the element when you click. After making the change, you update the state with the new value.

You can read more about this attribute here: https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Editable_content

Does this help? I can try to put together an example, but I am a bit swamped this week.

:grinning:

I’m trying to edit it in the same input that it was submitted.
I already am able when i click on it to see its value in the input field. But when i press add, it adds new item instead of updating the old one. That’s where i’m stuck.

If you look the code, i guess that i need a way in addColor to check if it’s key exists, to update that item. Any thoughts on that?

addColor(c) {
    // check for valid hex code with regex
if (this.state.color.match(/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i)) {
  console.log('Hex ok');
  var newColor = {
    text: this.state.color,
    key: Date.now(),
  };

  this.setState((prevState) => {
    return {
      colors: prevState.colors.concat(newColor)
    };
  });
} else {
  console.log('Hex NOT ok');
}

this.setState({
  color: ""
});
console.log(this.state.colors);

c.preventDefault();
}

onChange:

onChangeEdit(value) {
console.log(value);
this.setState({
  color: value
});
}

input field:

<input value={this.state.color}
            onChange={e => this.onChangeEdit(e.target.value)}
            placeholder="#000000"
            name="color" />

Hello Kirupa,
I am learning React following your book. It is really good.
I have a question regarding the ToDoList example. You have suggested two ways of adding new items to the array (one using unshift, and the one using concat).
When I type the code you have listed above, it works well. However, if I try to change the concat() method on the array “items” to “unshift” I start getting this error:

please send me edit part of this todo

Sir please send me code to edit the elements
my emai id is [email protected]
thank you