0

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.

2 Answers 2

1

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>

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

Comments

1

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}>

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.