M. Greco
M. Greco

Reputation: 49

What am I missing in this React to-do app that I'm making?

Yes, I'm new to React, and I'm trying to make the (infamous?) to-do app from scratch. So far I just have a todo component and then the main app. Here's my code - first is the Todo component:

import PropTypes from 'prop-types';

class Todo extends Component {

    static defaultProps = {
        completed: false,
    }

    static propTypes = {
        completed: PropTypes.bool.isRequired,
        // name: PropTypes.string.isRequired,
    }

    constructor(props){
        super(props)

        this.state = {
            completed: false,
        }
    }

    render(){
        return (
            <div>
                {!this.state.completed && <div>
                    <span>{this.props.name}</span>
                    <input type="checkbox" onClick={() => {this.markCompleted()}}/>
                </div>}
            </div>
        )
    }

    markCompleted = () => {
        this.setState((currentState) => {
            return {
                completed: !currentState.completed
            }
        })
    }
}

export default Todo;

And then the main App component...

import React from 'react';
import Todo from './Todo';
import './App.css';

class App extends React.Component {

  constructor(props){
    super(props)
      this.state = {
        todos: [{id: 1, name: 'take out trash'}, {id: 2, name: 'feed the cat'}]
      }
  }

  render(){
    let textInput;
    const todos = this.state.todos.map(todo => <Todo key={todo.id} name={todo.name} completed={todo.completed}/>)
    return (
      <div className="App">
         <input type="text" ref={(el) => {textInput = el}} />
         <button onClick={() => {this.addTodo(textInput.value)}}>Click to Add a Todo</button>
        {todos}
      </div>
    );
  }

  addTodo = (todoName) => {
    const newTodo = {id: this.state.todos.length + 1, name: todoName};
    const todos = this.state.todos.map(todo => <Todo key={todo.id} name={todo.name} completed={todo.completed}/>)
    todos.push(newTodo)
    console.log(this.state.todos);
      this.setState({
        todos
      })
      // console.log(todos)
    }
}

export default App;

So I'm left with a conundrum. On the initial render, the state of the todos array maps out just fine. However, when I type in a todo and click the button to add the todo, all of the names of the other todos vanish, and only the name of the last pushed todo appears. So if I were to add a todo "pay bills," then the checkboxes for "take out the trash" and "feed the cat" will still be visible, but the names of those todos will no longer be visible. I can click the checkboxes just fine to remove the todos as well, but I wish my todos array would cooperate. I think I'm also supposed to use .filter to properly filter out todos whose completed state is marked as true. Are the other names disappearing because of the todoName parameter I've added in the addTodo method? Is that causing all previous todo names to be undefined? I am really trying to learn, and I'm open to all help and suggestions to improve. I would request that we work with the code that I have already, as I've seen how other people have made the todo app before, and I don't want to just copy and observe someone else's code, because I feel that that detracts from my learning. Thanks in advance for any help!

Upvotes: 0

Views: 81

Answers (1)

yuri
yuri

Reputation: 3400

I did some corrections on the App class:

In addTodo, simplify it, you can push the state array like this:

  addTodo = (todoName) => {
    const newTodo = {id: this.state.todos.length + 1, name: todoName};
      this.setState({
        todos: [...this.state.todos, newTodo]
      })
      console.log(this.state.todos);
    }

Same for the render, you do not need that auxiliar todos you were using, just map the state like you were doing.

  render(){
    let textInput;
    return (
      <div className="App">
         <input type="text" ref={(el) => {textInput = el}} />
         <button onClick={() => {this.addTodo(textInput.value)}}>Click to Add a Todo</button>
        {this.state.todos.map(todo => <Todo key={todo.id} name={todo.name} completed={todo.completed}/>)}
      </div>
    );
  }

Upvotes: 1

Related Questions