1

I'm new to React JS (and JS, in general). Now I'm trying to code a simple task tracker.

So, I have all tasks in state element of MyTodoList class. There I draw each task separately with Task constant.

I want to implement adding a new task with 2 inputs: name and description.

I do it in MyTodoList with creating a new object (newTask), so that I can add it to state list later. However, I guess that I'm writing onChange method for input incorrectly. newTask seems to be updating inside the function (logged it in console), but it does not change outside (in input space there are no changes with typing). Obviously I cannot use setState as I want to update a non-state object (object is mutable, so I do not understand why it won't change).

I'm not sure whether I'm updating the object wrongly or whether my whole concept of adding new task is wrong. Would be grateful if you could explain me my mistakes.

Here's the code:

const TaskAdd = ({value, onChange, placeholder, name}) => {
  return (
      <input value={value} onChange={onChange} placeholder={placeholder} name={name}/>
  )
}


const Task = ({id, name,  description, completed}) => {
    const handleClick = () => {
    }
  
    return (
      <div className='task'>
        <h3>{name}</h3>
        <div>{description}</div>
        <div>{completed}</div>
        <button onClick={handleClick} className='button1'>CLICK</button>
      </div>
    )
  }


class MyTodoList extends React.Component {
  state = {
    tasks: [
      {
        id: 1,
        name: 'Walk the dog',
        description: 'Have to walk the dog today',
        completed: false,
      },

    ]
  }

  maxId = this.state.tasks[this.state.tasks.length - 1].id;

  newTask = {
    id: this.maxId,
    name: '',
    description: '',
    completed: false,
  }

  handleChange = (event) => {
    const {value, name} = event.currentTarget

    this.newTask[name] = this.newTask[name] + value
  }

  render () {
    return(
      <div>
        <header><h1>TO-DO</h1></header>
        <div className='addTask'>
          <h2>Let's add something new</h2>

          <TaskAdd value={this.newTask.name} onChange={this.handleChange} 
          placeholder='Name' name='name'/>

          <TaskAdd value={this.newTask.description} onChange={this.handleChange} 
          placeholder='Description' name='description'/>
          <p> {this.newTask.name}</p>

          <button className='button1'><h3>Add</h3></button>
        </div>
        <div>{this.state.tasks.map(task => <Task id={task.id} name={task.name} 
        description={task.description} completed={task.completed}/>)}
        </div>

      </div>
      )
  }

}

const App = () => {
  return (
    <MyTodoList />
  )
}

export default App;
2
  • Is state = { valid syntax with classes? Commented Apr 27, 2021 at 20:51
  • 1
    @TJ those are called "class fields". It's an upcoming part of javascript, and available now with a transpiler. Commented Apr 27, 2021 at 20:58

1 Answer 1

3

Obviously I cannot use setState as I want to update a non-state object

If you want the screen to update you have to use state. The setState function is the only* way to tell react that something change and it needs to rerender.

So, expand your state to have new task in it:

  state = {
    tasks: [
      {
        id: 1,
        name: 'Walk the dog',
        description: 'Have to walk the dog today',
        completed: false,
      },

    ]
    newTask: {
      id: 2,
      name: '',
      description: '',
      completed: false,
    }
  }

With that you'll need to update your render function to access it in state, as in:

<TaskAdd 
  value={this.state.newTask.name} 
  onChange={this.handleChange} 
  placeholder='Name'
  name='name'
/>

And then when you set state, make a copy instead of mutating:

handleChange = (event) => {
  const {value, name} = event.currentTarget

  this.setState({
    newTask: {
      ...this.state.newTask,
      [name]: this.state.newTask[name] + value
    }
  });
}

Your code didn't include an implementation for the add button, but when you do, you'll probably take this.state.newTask and add it to the end of this.state.tasks (you'll make a copy of the array, not mutate it), and then create a new object to replace this.state.newTask


*ok, technically there's forceUpdate, but don't use that.

Sign up to request clarification or add additional context in comments.

1 Comment

It's okay to use forceUpdate in some rare cases, but if you're asking this question, you really probably don't want to be using it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.