2

I building a simple todo app using the MERN stack with router and able to get it going except the edit part. I have 3 routes, the "Todos" page where i use axios to get the data from my express server route. The "Add" page for create new todo and the "Edit" page for editing and deleting. Here's my todos page where each todo has a button that takes the id as a url parameter unto the Edit page.

enter image description here

That little pencil/paper icon on each todo is a button link that get's the ID on click. Here's my Edit page with the data.

enter image description here

The warning:

enter image description here

Here's my Todos page i'm using a custom hook to fetch the data from express server route:

import React from 'react';
import useGetAPI from '../custom_hooks/useGetAPI';
import Todo from './todo_item/Todo';

const Todos = () =>{

    const data = useGetAPI('http://localhost:4000');

    return (
        <div className="page">
            <div className="page-header">
                <h1>Todo Lists</h1>
            </div>
            <div className="page-content">
                <ul className="todo-list">
                    {
                        data.map((todo)=><Todo todo={todo} key={todo._id}/>)
                    }
                </ul>
            </div>
        </div>
    );
}

export default Todos;

Here's my custom hooks for fetching data - used in Todos.

import {useState,useEffect} from 'react';
import axios from 'axios';

const useGetAPI = (url)=>{
    const [data,setData] = useState([]);

    useEffect(()=>{
        const fetchData = async ()=>{
            const response = await axios.get(url);
            const data = [...response.data];
            const error = response.error;
            if(error)
                console.log(error)
            else{
                console.log(data);
                setData(data);  
            }
        };
        fetchData();
    },[url])

    return data;
}

export default useGetAPI;

Here's my Edit Page

import React,{useState, useEffect, useContext, useCallback} from 'react';
import useGetApiWithParams from '../custom_hooks/useGetApiWithParams';
import {FaTelegramPlane} from 'react-icons/fa';
import axios from 'axios';
import { matchPath } from 'react-router'


const EditTodo = (props) =>{


const todoID = props.match.params.id;

    const [todo,setTodo] = useState(null);

    const responseData = useGetApiWithParams('http://localhost:4000/edit',todoID);
    console.log(`Todo id: ${todoID}`);

    /* Set default data from database */
    useEffect(()=>{
        setTodo(responseData);
    },[responseData,setTodo]);

    const [description,setDescription] = useState('');
    const [responsible,setResponsible] = useState('');
    const [priority,setPriority] = useState('');
    const [completed,setCompleted] = useState(false);


    const handleDescription = useCallback((e)=>{
        setDescription(e.target.value);
    },[setDescription]);

    const handleResponsible = useCallback((e)=>{
        setResponsible(e.target.value);
    },[setResponsible]);

    const handlePriority = useCallback((e)=>{
        setPriority(e.target.value);
    },[setPriority]);

    const handleCompleted = useCallback((e)=>{
        setCompleted(!completed);
    },[completed,setCompleted])

    const handleSubmit = useCallback((e)=>{     
        e.preventDefault();

        console.log('Form submitted');
        console.log(`Description ${description}`);
        console.log(`Description ${responsible}`);
        console.log(`Description ${priority}`);
        console.log(`Description ${completed}`);

        const updatedTodo = {
            description,
            responsible,
            priority,
            completed: false
        }

        axios.put(`http://localhost/4000/edit/${props.match.params.id}`, updatedTodo)
        .then(res=>console.log(res.data))
        .catch(function (error) {
            console.log(error);
        });

    },[description,responsible,priority,completed,props.match.params.id]);

    return (
        <div className="page">
            <div className="page-header">
                <h1>Edit Todo</h1>
            </div>
            <div className="page-content">
                <form id="edit-todo-form" className="todo-form" onSubmit={handleSubmit}>
                    <div className="form-group">
                        <label htmlFor="description">Description:</label>
                        <input id="description" type="text" className="form-control" onChange={handleDescription} value={responseData.description} />
                    </div>
                    <div className="form-group">
                        <label htmlFor="responsible">Responsible:</label>
                        <input id="responsible" type="text" className="form-control" onChange={handleResponsible} value={responseData.responsible} />
                    </div>
                    <div className="form-group">
                        <label htmlFor="priorities">Priorities:</label>
                        <div id="priorities" className="form-radios">
                            <label htmlFor="radio1" className="radio-label">
                                <input name="priorityOptions" type="radio" id="radio1" value={responseData.priority} checked={priority === 'Low'} onChange={handlePriority}/>
                                <span className="radiomark"></span>
                                <span className="radiotext">Low</span>
                            </label>
                            <label htmlFor="radio2" className="radio-label">
                                <input type="radio" id="radio2" value={responseData.priority} checked={priority === 'Medium'} onChange={handlePriority}/>
                                <span className="radiomark"></span>
                                <span className="radiotext">Medium</span>
                            </label>
                            <label htmlFor="radio3" className="radio-label">
                                <input type="radio" id="radio3" value={responseData.priority} checked={priority === 'High'} onChange={handlePriority}/>
                                <span className="radiomark"></span>
                                <span className="radiotext">High</span>
                            </label>
                        </div>
                    </div>
                    <div className="form-group">
                        <label htmlFor="todo_completed">Status:</label>
                        <div id="todo_completed">
                            <label htmlFor="checkcompleted" className="check-label">
                                <input type="checkbox" id="checkcompleted" value={responseData.completed} onChange={handleCompleted}/>
                                <span className="checkmark"></span>
                                <span className="checktext">Completed</span>
                            </label>
                        </div>
                    </div>
                    <div className="form-group">
                        <button type="submit" className="form-btn"><FaTelegramPlane />Save Changes</button>
                    </div>
                </form>
            </div>
        </div>
    );
}

export default EditTodo;

Here's my custom hook for fetching data based on the Todo ID i get from the url:

import { useState,useEffect } from 'react';
import axios from 'axios';

const useGetApiWithParams = (url,params)=>{
    const [data,setData] = useState([]);

    useEffect(()=>{
        const fetchData = async ()=>{
            const response = await axios.get(`${url}/${params}`);
            const data = response.data;
            const error = response.error;
            if(error)
                console.log(`Error: ${error}`)
            else{
                console.log(...data);
                setData(...data);   
            }
        };
        fetchData();
    },[url,params])

    return data;
}

export default useGetApiWithParams;

And the url with id param from MongoDB:

enter image description here

How to solve this? I tried the placeholder and set it's value to the response data from api but it only works for text boxes, what about the radio buttons and checkboxes? They don't seem to set the default value. For example the radio button for setting the priority value={responseData.priority} doesn't set the value as you can see here:

enter image description here

What i would like to happen is: from the Todos page on clicking the edit button, the edit page with a form already filled with values based on the todo id from the url parameter and be able to edit the values as well. Need help, thanks!

Nikhil Savaliya
  • 2,138
  • 4
  • 24
  • 45
Twirlman
  • 1,109
  • 2
  • 12
  • 30
  • 1
    The React errors in the console are referring to issues with Controlled vs Uncontrolled Components: https://reactjs.org/docs/forms.html#controlled-components. Have you read through that yet? – Aaron Jun 13 '19 at 15:48
  • 1
    Also, this question looks similar in nature to your issue: https://stackoverflow.com/questions/47012169/a-component-is-changing-an-uncontrolled-input-of-type-text-to-be-controlled-erro/47012342 – Aaron Jun 13 '19 at 15:50
  • 1
    Here is another: https://stackoverflow.com/questions/37427508/react-changing-an-uncontrolled-input – Aaron Jun 13 '19 at 15:52
  • 1
    Possible duplicate of [React - changing an uncontrolled input](https://stackoverflow.com/questions/37427508/react-changing-an-uncontrolled-input) – Aaron Jun 13 '19 at 15:53

0 Answers0