Building a Simple Todo List App in React


#22

Thank you for your reply. Yes, that’s true. I check it before. Its returning ‘undefined’. How it is possible, that I have the same code like on your tutorial and in my code is error?


#23

Is your state being properly initialized in the constructor? And is entries getting defined with:

<TodoItems entries={this.state.items}/>

?


#24

There was my mistake, thank you for your time.
Next time I will be more watchful.


#25

Glad you figured it out! :slight_smile:


#26

hi there, love the walkthrough but having some trouble.

At the part whee we try to get the tasks to print on the page, but when i hit enter they are only logging to the console.

// todoitems.js

import React, { Component } from 'react';

class TodoItems extends Component {
  constructor(props, context) {
    super(props, context);

  

    this.createTasks = this.createTasks.bind(this);
  }

  createTasks(item) {
    return <li 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/TodoItems';
import '../TodoList/TodoList.css';

class TodoList extends Component {
  constructor(props, context) {
    super(props, context);

    this.state = {
      items: []
    };

    this.addItem = this.addItem.bind(this);

  }

  addItem(e) {
    const itemArray = this.state.items;

    if (this._inputElement.value !== '') {
      itemArray.unshift({
        text: this._inputElement.value,
        key: Date.now()
      });

      this._inputElement.value = '';
    }
    console.log(itemArray);

    e.preventDefault();
  }

  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}/>
      </div>
    );
  }
}
export default TodoList;

thanks! cannot see what is going wrong here!


#27

Hi all,

I struggled with the deleteItem method for a while. This was very helpful in explaining how filter works


#28

I just noticed this. If you add a console.log in your render methods, are you seeing them getting called?


#29

I have a qustion. Why file “index.css” doesn’t work at all? I followed whole tutorial and everything works fine, but this one file doen’t change anything in page look at all. :frowning: It doesn’t even matter if I comment this file or not. It look like it isn’t loaded anyway.

EDIT: I just needed to center it and worked centering it in html file, but I still wonder why it doesn’t work. I attached this css file as was metioned in the tutorial. :disappointed_relieved:


#30

It should work. For example it adds a blue background color to the page as seen in the live version here:

https://www.kirupa.com/react/examples/todolist/index.html

Do you not have a blue background in your version?


#31

No. :frowning: It looks like this:


I centered and changed color of other elements in their css file. Index.css is without changes.


#32

Yup, it’s definitely not being picked up. Given that the other CSS file is working, it means it can work. The question is why its not now.

Some things to check:

  • Is the import statement correctly targeting the index file in the right location (in same folder)?
  • Is capitalization correct for the filename file path?
  • Are there any syntactic errors in the CSS file?

#33
import "./index.css";

When I changed this import into not existing, it gave me error in browser. I suppose this file is being imported after all.
Content of my index.css file:

body {
  padding: 50px;
  background-color: #66CCFF;
  font-family: sans-serif;
}
#container {
  display: flex;
  justify-content: center;
}

All files exept html are inside src directory. :frowning:

EDIT: I have found the way to make this work: I needed to put css file into public directory and refer it this way in index.html:

  <head>
    <link rel="stylesheet" type="text/css" href="mystyle.css">
  </head>

It is not exactly what I wanted to do, but now it finally works. Thank you for replies and your time!

EDIT2:
I ALSO FOUND THE REASON why it didn’t work from the beggining. When I coppied code from tutorial, it added some wierd, two-spaces long tabulator inside css file, so tags weren’t closed and I didn’t notice it in my text editor. D: I changed it into real two spaces and it started to work perfectly!


#34

I have the same issue. When I use console.log for todoEntries, it displays but not for listItems.


#35

Hi! Many thanks for your effort in building a good tutorial. Kudos for trying to focus as much attention on the React itself as needed (by providing your own CSS styles, for instance, or using create-react-app instead of forcing a reader to deep dive into another concept that is not relevant to react apparently).

I’d propose to reduce the noise even more by:

  • always using arrow expression class method declaration to avoid redundant binding (thus confusing the reader if he wants to add a new method and forgets about binding)
  • removing a react dependency to the animation library
  • using entries.map directly in the render method, not an auxiliary method. This resembles the well known templating standards like vue.js/angular/moustache and leads to less confusion.

I also noticed you are using a mutable operation to manipulate the state, which is a bad thing (although happens to work good on your code). I think that a lot of confusion in the comments is related to that. Speaking about:

var entriesList = this.state.entries;
entriesList.unshift({...new entry...});
this.setState({entries: entriesList});

unshift will cause a state mutation, because even though you operate on entriesList variable, it points on the one of state’s field. It would be much cleaner with:

this.setState({ entries: this.state.entries.concat({... new entry ...}) });

Hope you find my advices helpful or reasonable, but of course you don’t have to 100% agree with all of them. I am open to your comments and the discussion, or even better ideas!


#36

Hi @zdanowiczkonrad!
Thanks for the detailed answer. I agree with all of your points. Comments for them:

always using arrow expression class method declaration to avoid redundant binding (thus confusing the reader if he wants to add a new method and forgets about binding)

I tried to be consistent with the React documentation here. I discussed the various options for auto-binding in a different tutorial, but for consistency I describe explicitly using bind in the constructor as needed.

removing a react dependency to the animation library

Adding a dependency for something that could be done using just regular CSSTransitionGroup does seem a bit heavyweight, but this library was just too simple. I also wanted to highlight how easy it is to add a 3rd party library and integrate it as part of your app.

using entries.map directly in the render method, not an auxiliary method. This resembles the well known templating standards like vue.js/angular/moustache and leads to less confusion.

I use map in later tutorials, but I kept it in its expanded form here. One of the reasons is that these tutorials target a beginner audience, and I try to minimize the number of concepts they would be unfamiliar with. For more advanced React tutorials, map will be used more and more.

Lastly, good catch on the setState method. @senocular brought that up as well. I’ll update the code by using both concat and prevState, as recommended.

:slight_smile:


#37

I am getting this same error. What is the resolution?


#38

There may be a copy/paste issue. Can you try writing that method manually and see if it fixes it?


#39

Hi Kirupa,
I did type manually, here is the code. Do you see a syntax error?

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

    this.setState({
        items:filteredItems
    });
}

#40

Can you attach your JS file to this response? I’ll do a diff and see if there is some random spacing or character throwing things off. What you’ve posted looks good to me!


#41

yes, here it is.

TodoList.js (1.5 KB)