You don't have access to this class

Keep learning! Join and start boosting your career

Aprovecha el precio especial y haz tu profesión a prueba de IA

Antes: $249

Currency
$209
Suscríbete

Termina en:

0 Días
12 Hrs
40 Min
3 Seg

Obtener y editar TODOs

23/30
Resources

How to implement the edit function in a custom hook in React?

Developing software using React is undoubtedly an exciting journey. In this section, we will explore how to implement the edit functionality in a list of "To Do's" using a custom hook in React. This is a critical aspect, as it will allow us to update the items in a list in a direct and efficient way, ensuring that our project reflects changes dynamically.

How to modify the custom hook to include the edit function?

First of all, we must add the edit functionality to our custom hook useToDos. This hook already has functions to add, complete and delete "To Do's". To include the edit option, you will need to slightly modify the internal logic of the hook. I recommend building on the logic used for completing tasks and adapting it to create an editToDo function.

// In your hook useToDosconst editToDo = (id, newText) => { // logic to find the index and update the text // similar to the completeToDo function};

This method will take an ID and a newText assigned to that "To Do", modifying the text property without altering the ID of the original "To Do". Do not forget to return this method from your hook to be able to use it in your components.

How to apply the edit function in our components?

With the editToDo function ready in the custom hook, it is time to implement it in the user interface. Here, it is fundamental to define how and when this function will be invoked, making sure that the components responsible for the routes have access to it.

  1. Import the custom hook: When you are in the editToDoPage, import useToDos and access editToDo.

    // In the file editToDoPage.jsxconst { stateUpdaters: { editToDo }} = useToDos();
  2. Development of the submit event: At the time of submitting the form, the new text and the ID of the "To Do" will be captured from the URL and sent to the editToDo function.

    const handleSubmit = (newText) => { const id = ... // Extract the ID from the URL using useParams editToDo(id, newText); // redirect to home or some other action};

With just these steps, a robust functionality of editing "To Do's" will be enabled, optimizing task management and bringing dynamism to your application.

How to handle URL parameters to obtain specific information?

In route-based applications, it is crucial to handle the parameters included in the URL to extract meaningful information, such as the ID of the "To Do's". React Router DOM offers an elegant solution through the useParams hook.

  1. Using useParams: By using this hook, we can access the parameters indicated in the URL easily and directly. Its use is necessary to capture the ID of the "To Do" and apply it in the editToDo method.

    import { useParams } from 'react-router-dom';
    const { id } = useParams();
  2. Conversion to number: It is essential to convert this extracted parameter to number, since React handles URL parameters as strings by default. This avoids future issues when interacting with indexes or other numeric elements.

    const idNumber = Number(id);

These processes, applied with care and precision, ensure the correct handling of tasks and the continuous enrichment of our application, providing detailed and satisfying final experiences for users.

What questions remain to be resolved at the end of the implementation?

While basic editing functionality has been addressed, the development process always opens the door to new questions and future enhancements. Here are some areas to consider:

  • Display of current text: When editing a "To Do", it is desirable to have the form load with the current default text to simplify editing.
  • State handling errors: Ensure that the functionality does not carry errors in updating or rendering state.
  • Improved event handling: Explore techniques to improve efficiency in event and form handling.

Each progression in React is an invitation to mastery and excellence in development, a journey that undoubtedly promises innovation and satisfaction. With these fundamentals, you are more ready than ever to continue exploring and honing your React skills!

Contributions 7

Questions 1

Sort by:

Want to see more contributions, questions and answers from the community?

Si pruebas agregar otro elemento. Problabemente, verás la sobreescritura de un anterior elemento.

Habia debugueado con muchos console.log hasta darme cuenta que en la asignación de nuevo ID llegaba vacío. Pensé que la variable todos se encontraba en otro scope y habia un error… 😖😖😖 estuve horas así sabiendo el porque, pero sin saber como. Para nuestra buena suerte la solución y explicación a ese error se encuentran en el siguiente video.

En el proceso de editar un todo me salía un error de compilación que no identificaba el id, me ayudó utilizar lo siguiente en la página de EditTodo Page:

const {id} = useParams()

Que extrae el id de los parámetros, pero como string, de tal modo que en el useTodos en el editTodo habría que hacerle un parseInt a ese id para que funcione

Hola a todos, les dejo el código de la clase en TS por si alguien se aventuro como yo en TS y quiere comparar o tambien agregar:

import { TodoForm } from "../components/TodoForm"
import { useTodos } from "../hooks/useTodos"

const NewTodo = () => {

    const { addTodo } = useTodos().stateUpdaters;

    return (
        <div className="form-container">
            <TodoForm 
                submitEvent={(newTodoText)=>{addTodo(newTodoText)}}
            />
        </div>
    )
}

export { NewTodo }

import { useParams } from "react-router-dom";
import { TodoForm } from "../components/TodoForm"
import { useTodos } from "../hooks/useTodos";

const EditTodo = () => {

    const { id } = useParams();
    const { editTodo } = useTodos().stateUpdaters;   

    return (
        <div className="form-container">
            <TodoForm 
                submitEvent={(newTodoText)=>{editTodo((id as string), newTodoText)}}
            />
        </div>
    )
}

export { EditTodo }

import { Dispatch, SetStateAction, useState } from "react";
import { useLocalStorege } from "./useLocalStorage";

export interface ITodo {
  text:string,
  completed:boolean,
  id: string
}

interface State {
  searchedTodos: ITodo[]
  completedTodos: number,
  totalTodos: number,
  searchValue: string,
  loading: boolean,
  error: boolean,
  openModal: boolean,
}

interface StateUpdaters {
  setSearchValue: Dispatch<SetStateAction<string>>
  addTodo: (v:string)=>void
  completeTodo: (v:string)=>void,
  editTodo: (id:string, v:string)=>void
  deleteTodo: (v:string)=>void,
  setOpenModal: Dispatch<SetStateAction<boolean>>
  sincronizeTodos: ()=>void
}

const useTodos = (): { state: State, stateUpdaters: StateUpdaters } => {
  
  const {
    item: todos,
    saveItem: saveTodos,
    loading,
    error,
    sincronizeItem:  sincronizeTodos,
  } = useLocalStorege<ITodo[]>('TODOS_V2', []);

  const completedTodos:number = todos.filter(todo=>todo.completed).length;
  const totalTodos:number = todos.length;
  const [searchValue, setSearchValue] = useState<string>('');
  const [openModal, setOpenModal] = useState(false);

  const searchedTodos = todos.filter(todo=> todo.text.toLowerCase().includes(searchValue.toLowerCase()));

  const addTodo = (text:string) => {
    const id = crypto.randomUUID();
    const newTodos = [...todos]
    newTodos.push({
      text,
      completed: false,
      id
    })
    saveTodos(newTodos)
  }

  const editTodo = (id: string, text:string) => {
    const newTodos = todos.map(todo=>{
      if(todo.id === id) return {
        ...todo,
        text
      };
      return todo;
    })
    saveTodos(newTodos)
  }
  
  const completeTodo = (id: string) =>{
    const newTodos = todos.map(todo=>{
      if(todo.id === id) todo.completed = !todo.completed;
      return todo;
    })
    saveTodos(newTodos);
  }

  const deleteTodo = (id: string) => {
    const newTodos = todos.filter(todo=>todo.id !== id);
    saveTodos(newTodos);
  }

  const state = {
    searchedTodos,
    completedTodos,
    totalTodos,
    searchValue,
    loading,
    error,
    openModal,
  }

  const stateUpdaters = {
    addTodo,
    setSearchValue,
    completeTodo,
    editTodo,
    deleteTodo,
    setOpenModal,
    sincronizeTodos,
  }

  return {
    state,
    stateUpdaters,
  }
}

export { useTodos }
import { FC, FormEvent, useState } from "react"
import { useLocation, useNavigate } from "react-router-dom";
import './TodoForm.css'

interface Props {
    submitEvent: (v:string)=>void,
}

const TodoForm:FC<Props> = ({ submitEvent }) =>{

    const navigate = useNavigate();
    const location = useLocation();
    const [newTodoText, setNewTodoText] = useState('');

    const onCancel = ()=>{
        navigate('/');
    }

    const onSubmit = (e:FormEvent<HTMLFormElement>) => {
        e.preventDefault();
        submitEvent(newTodoText);
        navigate('/');
    }

    return(
        <form onSubmit={onSubmit}>
            <label>{location.pathname === '/new'? 'Escribe tu nueva tarea pendiente':'Edita tu tarea pendiente'}</label>
            <textarea 
                placeholder="Hola, que haremos hoy?"
                value={newTodoText}
                onChange={(event)=>{
                    setNewTodoText(event.target.value)
                }}
            />
            <div className="TodoForm-buttonContainer">
                <button
                    type="button"
                    onClick={onCancel}
                    className="TodoForm-button TodoForm-button--cancel"
                >
                    Cancelar
                </button>
                <button
                    type="submit"
                    className="TodoForm-button TodoForm-button--add"
                >
                    {location.pathname === '/new'? 'Añadir':'Editar'}
                </button>
            </div>
        </form>
    )
}

export { TodoForm }

Obtener y editar TODOs

.
En el componente EditTodoPage vamos a necesitar obtener el id y el nuevo texto que queremos cambiar al TODO.
.
Para obtener el id utilizamos el react hook useParams, mientras que para obtener el texto pasaremos por propiedad a una función submitEvent a TodoForm, donde este será quien internamente pasará el texto a la función editTodo.
.
Por cierto, en TodoForm vamos a mover la navegación debajo y después de llamar a props.submitEvent.
.

import React from 'react';
import { useParams } from 'react-router-dom';
import { TodoForm } from '../../ui/TodoForm';
import { useTodos } from '../useTodos';

function EditTodoPage() {
  const params = useParams();
  const id = Number(params.id);
  
  const { stateUpdaters } = useTodos();
  const { editTodo } = stateUpdaters;
  
  return (
    <TodoForm
      label="Edita tu TODO"
      submitText="Editar"
      submitEvent={(newText) => editTodo(id, newText)}
    />
  );
}

export { EditTodoPage };
// TodoForm/index.js
const onSubmit = (event) => {
    event.preventDefault();
    props.submitEvent(newTodoValue);
    navigate('/');
  };

.
En NewTodoPage se sigue la misma lógica, donde lo único que cambia es la función que se utiliza en submitEvent que en este caso es addTodo y se le pasa solo el texto del nuevo TODO.
.

import React from 'react';
import { TodoForm } from '../../ui/TodoForm';
import { useTodos } from '../useTodos';

function NewTodoPage() {
  const { stateUpdaters } = useTodos();
  const { addTodo } = stateUpdaters;
  
  return (
    <TodoForm
      label="Escribe tu nuevo TODO"
      submitText="Añadir"
      submitEvent={(text) => addTodo(text)}
    />
  );
}

export { NewTodoPage };

.
Finalmente, el mayor cambio se ve reflejado en useTodos.js, donde eliminamos todo lo relacionado con modales y portales; añadimos una función editTodo que es muy similar a la de completeTodo, solo que en lugar de cambiar completed, modificamos el text del TODO. Por último, retornamos a editTodo dentro del objeto stateUpdaters.
.

import React from 'react';
import { useLocalStorage } from './useLocalStorage';

function useTodos() {
	...
  const editTodo = (id, newText) => {
    const todoIndex = todos.findIndex(todo => todo.id === id);
    const newTodos = [...todos];
    newTodos[todoIndex].text = newText;
    saveTodos(newTodos);
  };
  ...
  const stateUpdaters = {
    setSearchValue,
    addTodo,
    completeTodo,
    editTodo,
    deleteTodo,
    sincronizeTodos,
  };

  return { state, stateUpdaters };
}
...

export { useTodos };
🤗 **-English ver. branch:** <https://github.com/SebaMat3/react-todo/tree/feat/update-todo-management> **Branch name:** feat/update-todo-management **Commit message:** git commit -m "feat: improve todo management with IDs and edit functionality \- Implement editTodo function to modify existing todos \- Remove modal-related state and comments \- Add support for finding and updating todos by ID \- Implement ID-based todo operations (edit, complete, delete)"
Reto futuro solucionado con unas mejoras, cuando el usuario borre por completo el texto vuelve y muestra su texto anterior, una vez lo guarde el nuevo texto este pasa a ser el texto que tiene el usuario y si borra todo aparecerá este, PD intenten publicar un gift pero parece que no se puede y si no seria mas entendible mi explicacion![]() ![](https://www.canva.com/design/DAGC6Lljlwk/TjcDCwi7fpVStjffRBjm4g/edit?utm_content=DAGC6Lljlwk\&utm_campaign=designshare\&utm_medium=link2\&utm_source=sharebutton)![]()![]()![](https://static.platzi.com/media/user_upload/image-f64b70c5-28bb-4baf-a7d4-ff5f115d7032.jpg)![](https://static.platzi.com/media/user_upload/image-60a41a02-56e9-4771-9759-fa5ad489dd07.jpg) ![](https://static.platzi.com/media/user_upload/image-e03d4400-1660-4067-ab27-5b8b18b27490.jpg) ![](https://static.platzi.com/media/user_upload/image-85a544e0-1244-4bc8-97e5-8c1a866e18b5.jpg) ![](https://static.platzi.com/media/user_upload/image-59e8e2ba-5dcb-4a41-b9ef-f0d21947965b.jpg) ![](https://static.platzi.com/media/user_upload/image-68811026-6c55-42bf-ae2f-422bbda1b237.jpg)![](https://static.platzi.com/media/user_upload/image-388d0c6b-315a-4698-987a-4f156b5c7538.jpg)

Esa parte, la de rellenar el textarea con el texto previo de todo es la que no he terminado. El resto lo pude hacer antes de ver la clase.