0

I am very new to react and node, I have managed to create an API for a simple todo list. I have fetched the data from the api and presenting it on the screen.

If I leave the dependency array empty on the useEffect() hook it will only render once and doesn't loop. But If I add a new Todo it will not update the list unless I refresh. So I put the todos state into the dependency array, this will then show the new item when I add it but if I look at the network tab in the dev tools its hitting the api in an infinite loop. What am I doing wrong ?

here is the code:

App

import React, { useState, useEffect } from "react";
import Todo from "./components/Todo";
import Heading from "./components/Heading";
import NewTodoForm from "./components/NewTodoForm";

const App = () => {
  const [todos, setTodos] = useState([]);

  useEffect(() => {
    const getTodos = async () => {
      const res = await fetch("http://localhost:3001/api/todos");
      const data = await res.json();
      setTodos(data);
    };

    getTodos();
  }, []);

  return (
    <div className="container">
      <Heading todos={todos} />
      <section className="todos-container">
        <ul className="todos">
          {todos.map((todo) => (
            <Todo key={todo._id} todo={todo} />
          ))}
        </ul>
      </section>
      <section className="todo-form">
        <NewTodoForm />
      </section>
    </div>
  );
};

export default App;

Heading

import React from "react";

const Heading = ({ todos }) => (
  <header>
    <h1>Todos</h1>
    <p>
      {todos.length} {todos.length === 1 ? "Item" : "Items"}
    </p>
  </header>
);

export default Heading;

Todo

import React, { useState } from "react";

const Todo = ({ todo }) => (
  <li>
    {todo.name}
    <input type="checkbox" />
  </li>
);

export default Todo;

NewTodoForm

import React, { useState } from "react";
import { Plus } from "react-feather";

const NewTodoForm = () => {
  const [formData, setFormData] = useState({
    name: "",
    completed: false,
  });

  const { name } = formData;

  const handleOnChange = (e) => {
    setFormData({
      ...formData,
      [e.target.name]: e.target.value,
    });
  };

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

    await fetch("http://localhost:3001/api/todos", {
      method: "POST",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json",
      },
      body: JSON.stringify(formData),
    });

    setFormData({
      name: "",
      completed: false,
    });
  };
  return (
    <form onSubmit={handleSubmit}>
      <div className="form-control">
        <Plus className="plus" />
        <input
          name="name"
          type="text"
          placeholder="Add New Item"
          onChange={handleOnChange}
          value={name}
        />
        <button>Add</button>
      </div>
    </form>
  );
};

export default NewTodoForm;

If I comment all the components out and only have the App component it still infinite loops when I add todos to the dependency array of the useEffect() hook.

3
  • 1
    It will run into infinite loop because you are setting todo inside useEffect and giving the same as dependency Commented Jul 7, 2020 at 14:32
  • move this getTodos to outside of your useEffect Commented Jul 7, 2020 at 14:37
  • 1
    when you add a new todo its not good to fetch all the todos, you just need to fetch the newly created todo only so you might need to change the way you thinking, either you can get saved todo as a return from savetodo function and add that to the existing todos state or else fetch the only todo that you need as a seperate request Commented Jul 7, 2020 at 14:47

1 Answer 1

2

So instead of giving that as a dependency write the function outside the useEffect so that you can call that function after you add a todo

Example:

  const getTodos = async () => {
      const res = await fetch("http://localhost:3001/api/todos");
      const data = await res.json();
      setTodos(data);
    };

 useEffect(() => {
    getTodos();
  }, []);

So getTodos will only run once initially and runs again only on the onSubmit or onClick of your Todo, So, just call getTodos function onSubmit or onClick

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

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.