Marina
Marina

Reputation: 187

How to edit object (todo) in array? (reactjs)

So I have this to-do list app and I'm working on editing the name of to-do.

When I click on button edit I get the value of the name in input field, and when I type something in that field and press enter, it should change/update to-dos' name, but right now it is only adding new to-do.

Here is code I have so far that is working, I deleted all the unsuccessful attempts. I have no more ideas.

import React, { useRef, useReducer } from 'react'

function App() {

    const inputRef = useRef<HTMLInputElement | any>(null)

    const handleSubmit = (e: any) => {
        e.preventDefault()
        inputRef.current?.value !== "" && dispatch({ type: 'ADD_TODO', payload: inputRef.current?.value })
        inputRef.current && (inputRef.current.value = "")
    }

    const [todo, dispatch] = useReducer((state: any, action: any): any => {
        switch (action.type) {
            case 'ADD_TODO':
                return [...state, { id: state.length, name: action.payload, isCheck: false }]
            case 'CHECK_TODO':
                return state.filter((item: any, index: any): any => {
                    if (index === action.id) {
                        item.isCheck = !item.isCheck
                    }
                    return item
                })
            case 'DELETE_TODO':
                return state.filter((item: any, index: any) => index !== action.id)
            case 'EDIT_TODO':
                inputRef.current.focus()
                inputRef.current.value = action.payload
                return state
        }
    }, [])

    const todos = todo.map((item: any, index: number) => {
        return (
            <li key={index}>
                <input type="checkbox" checked={item.isCheck} onChange={() => dispatch({ type: "CHECK_TODO", id: index })} />
                {item.name}
                <button onClick={() => dispatch({ type: 'EDIT_TODO', id: index, payload: item.name })}>edit</button>
                <button onClick={() => dispatch({ type: "DELETE_TODO", id: index })}>x</button>
            </li>
        )
    })

    return (
        <div>
            <form onSubmit={handleSubmit}>
                <input
                    type="text"
                    placeholder='Buy milk'
                    ref={inputRef}
                />
            </form>
            <ul>{todos}</ul>
        </div>
    )
}

export default App

EDIT

Also, new button can be added for submit editing instead of pressing enter, as an option.

Upvotes: 0

Views: 721

Answers (2)

gautamits
gautamits

Reputation: 1292

You have to keep track of which id/index you are editing so that you can identify whether you are updating or adding value in state. Following code might help. I removed typescript though.

function App() {

  const inputRef = React.useRef(null)
  const [editingIndex, setEditingIndex] = React.useState(null)

  const handleSubmit = (e) => {
    e.preventDefault()
    if(inputRef.current && inputRef.current.value !== ""){
      dispatch({ type: 'ADD_TODO', payload: inputRef.current.value, id: editingIndex})
    }
    inputRef.current && (inputRef.current.value = "")
  }

  const [todo, dispatch] = React.useReducer((state, action) => {
    switch (action.type) {
      case 'ADD_TODO':
        setEditingIndex(null)
        const tempState = [...state]
        if(action.id){
          tempState[action.id] = { ...tempState[action.id], name: action.payload }
        }
        else{
          tempState.push({ id: action.id || state.length, name: action.payload, isCheck: false })
        }
        return tempState
      case 'CHECK_TODO':
        return state.filter((item, index) => {
          if (index === action.id) {
            item.isCheck = !item.isCheck
          }
          return item
        })
      case 'DELETE_TODO':
        return state.filter((item, index) => index !== action.id)
      case 'EDIT_TODO':
        inputRef.current.focus()
        inputRef.current.value = action.payload
        return state
    }
  }, [])

  const todos = todo.map((item, index) => {
    return (
      <li key={index}>
        <input type="checkbox" checked={item.isCheck} onChange={() => dispatch({ type: "CHECK_TODO", id: index })} />
        {item.name}
        <button onClick={() => {setEditingIndex(index); dispatch({ type: 'EDIT_TODO', id: index, payload: item.name })}}>edit</button>
        <button onClick={() => dispatch({ type: "DELETE_TODO", id: index })}>x</button>
      </li>
    )
  })

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <input
          type="text"
          placeholder='Buy milk'
          ref={inputRef}
        />
      </form>
      <ul>{todos}</ul>
    </div>
  )
}

ReactDOM.render(
    <App />,
  document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Upvotes: 1

D. Smith
D. Smith

Reputation: 749

Even though you are populating the input with the todo's name when you click the edit button, in the end the onSubmit is calling the handleSubmit function which calls the ADD_TODO reducer.

One thing you can do is set an "edit mode" flag, and change how handleSubmit does it's dispatching and make a new type like UPDATE_TODO using the index. Or make that a new function and call the appropriate one based on the update flag, like so:

<form onSubmit={editMode ? handleEdit : handleSubmit}>

Upvotes: 1

Related Questions