0

in React app I'm trying to increment +1 to the id field of each object in the array.

function App() {
  const[todos, setTodos] = useState([
    {
      id: 1,
      title: 'Take out the trash',
      completed: false
    },
    {
      id: 2,
      title: 'Dinner',
      completed: false
    },
    {
      id: 3,
      title: 'Meeting with boss',
      completed: false
    }
  ]);

  function onClick(){
    setTodos(todos => todos.map(todo => todo.id = todo.id + 1))
  }

  return (
    <div className="App">
      <div className="todo-list">
        <ul>
          {todos.map(todo => <li>{todo.id},{todo.title},{String(todo.completed)}</li>)}
        </ul>
        <button onClick={()=>onClick()}
        >Update
        </button>
      </div>
    </div>
  );
} 

On the initial rendering of the page list of objects is displayed properly. On first click of the button they change to this:

,,undefined
,,undefined
,,undefined

On 3rd click to this error:

TypeError: Cannot create property 'id' on number '2'

did I miss something about the map function ?

0

4 Answers 4

4
function onClick(){
  setTodos(todos => todos.map(todo => todo.id = todo.id + 1))
 }

You're returning the todo.id from your map function, therefore you're setting todos as an array of ids [2, 3, 4]. Then when you try to access todo.id, it is undefined as number.id is undefined`.

You're also mutating state by saying todo.id = .... If you find yourself using the assignment oprerator (=) when setting state, you may be doing something wrong. You should never have state = ....

You want to do something like this:

function onClick(){
  setTodos(prev => prev.map(t => ({ ...t, id: t.id + 1 })))
 }

I've also renamed todos to prev as you're creating a shadowed variable name.

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

6 Comments

I think return statement is compulsory when you put block there.
@MuhammadHassanKhan but there's no blocks used. It's just an inlined Object literal.
How did TypeError occur then?
I've updated You're not returning anything to You're returning the todo.id as the return is implicit as Muhammad pointed out.
Cool, now I've learned that todo that is being currently iterated over by map is there to just reference it, but wont actually be added to the new array. Gotta create new whole object that i want to be added to new array. Thanks
|
1

Map function iterate to each value and return the mapped value you computed in the iterator.

Your code converts array of todo objects to array of incremented todo ids like [2,3,4] so when next your map method runs it gives the error you are facing. As first time your map has converted objects to integers.

You should update your map method something like this: todos.map(todo => ({...todo, id: todo.id + 1}))

Note: Also you don’t need to write todos => in your setTodos hook.

1 Comment

Nope, see the error carribean posted i.e TypeError.
1

You actually don't return new todo entity in there, but just return id property, that's is a bug. Here's is a fix:

  function onClick(){
    setTodos(todos => todos.map(todo => ({ ...todo, id: todo.id + 1 })))
  }

Comments

0

Your map function not return a object but I think you should use the reduce JavaScript function

function onClick() {
    const reducerFunction = (r, v) => r.concat({ ...v, id: v.id + 1 })
    setTodos(
      todos.reduce(reducerFunction, [])
    );
}

I also think you should try the useReduce Hooks, in this case generate solutions more clean.

An alternative to useState. Accepts a reducer of type (state, action) => newState, and returns the current state paired with a dispatch method. (If you’re familiar with Redux, you already know how this works.)

useReducer is usually preferable to useState when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one. useReducer also lets you optimize performance for components that trigger deep updates because you can pass dispatch down instead of callbacks.

Comments

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.