Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React: Confusing behavior with useEffect and/or useState hooks

React noob here. I'm trying to make a simple practice app to help with my React learning and I'm getting a lot of odd behavior. It's a Todo app with multiple todo lists to choose from. The behavior I'm going for is a list of Todo lists, where you select one and the todo items for that (like wunderlist/msft todo). Select a different list and it's todo items show, etc. At this point it's using static json where each item has a child array.

useEffect - I’m trying to use this to load the data. It keeps complaining about missing dependencies. When I add them it complains about that too. It complains if I use and empty array for the second parameter and seems to fire multiple times.

useState - I'm using this to store the data. It's initialized to an empty array. But render is firing before useEffect and it's saying my data is undefined so my second list never renders.

I have several console.logs in the code and they're all firing multiple times.

I'm sure it's just noob mistakes but I'm pretty stumped at this point. Here's my code:

data/Todo.js

const TodoData = [
  {
    Id: 1,
    Title: "Groceries",
    TodoList: [
      {
        Id: 1,
        Title: "Apples"
      },
      {
        Id: 2,
        Title: "Oranges"
      },
      {
        Id: 3,
        Title: "Bananas"
      }
    ]
  },
  {
    Id: 2,
    Title: "Daily Tasks",
    TodoList: [
      {
        Id: 11,
        Title: "Clean Kitchen"
      },
      {
        Id: 12,
        Title: "Feed Pets"
      },
      {
        Id: 13,
        Title: "Do Stuff"
      }
    ]
  },
  {
    Id: 3,
    Title: "Hardware Store",
    TodoList: []
  },
  {
    Id: 4,
    Title: "Costco",
    TodoList: [
      {
        Id: 21,
        Title: "Diapers"
      },
      {
        Id: 22,
        Title: "Cat Food"
      },
      {
        Id: 23,
        Title: "Apples"
      },
      {
        Id: 24,
        Title: "Bananas"
      }
    ]
  },
  {
    Id: 5,
    Title: "Work",
    TodoList: [
      {
        Id: 34,
        Title: "TPS Reports"
      }
    ]
  }
];

export default TodoData;

App.Js

import React, { useEffect, useState } from "react";
import TodoData from "./data/Todo.js";

function App() {
  const [todoData, setTodoData] = useState([]);
  const [todoDetails, setTodoDetails] = useState([]);

  useEffect(() => {
    if (todoData.length === 0) {
      getTodoData();
    }
  }, [todoData]);

  const getTodoData = () => {
    setTodoData(TodoData);
    console.log("getting todo data");
    getTodoDetails(1);
  };

  const getTodoDetails = id => {
    const result = TodoData.filter(x => x.Id === id);
    console.log(result[0]);
    setTodoDetails(result[0]);
  };

  const handleClick = e => {
    const selectedId = Number(e.target.getAttribute("data-id"));
    getTodoDetails(selectedId);
  };

  return (
    <div className="App">
      <div className="container-fluid">
        <div className="row">
          <div className="list-group col-md-4 offset-md-1">
            {todoData.map(todos => (
              <button
                key={todos.Id}
                data-id={todos.Id}
                className="btn list-group-item d-flex justify-content-between align-items-center"
                onClick={handleClick}
              >
                {todos.Title}
                <span className="badge badge-primary badge-pill">
                  {todos.TodoList.length}
                </span>
              </button>
            ))}
          </div>
          <div className="col-md-6">
            <ul className="list-group">
              <h2>{todoDetails.Title} List</h2>

              {console.log(todoDetails.TodoList)}

              {/* this fails miserably */}
              {/* {todoDetails.TodoList.map(details => (
                <li className="list-group-item" key={details.Id}>
                  {details.Title}
                </li>
              ))} */}

              <li className="list-group-item">Cras justo odio</li>
              <li className="list-group-item">Dapibus ac facilisis in</li>
              <li className="list-group-item">Morbi leo risus</li>
            </ul>
          </div>
        </div>
      </div>
    </div>
  );
}

export default App;
Demo: https://codesandbox.io/s/interesting-dan-rdoyh?fontsize=14&hidenavigation=1&theme=dark
like image 771
cdubone Avatar asked Jul 07 '26 18:07

cdubone


1 Answers

Q : useEffect - I’m trying to use this to load the data. It keeps complaining about missing dependencies

A : you can ignore it via eslint (react-hooks/exhaustive-deps), there is nothing to worry about


Q : useState - I'm using this to store the data. It's initialized to an empty array. But render is firing before useEffect and it's saying my data is undefined so my second list never renders.

A : Do read the comments in code, hope that will clear your doubts

// you are intializing `todoDetails` with array, when there will be object
// hence the error while mapping inside the html/jsx
// const [todoDetails, setTodoDetails] = useState([]);

// init with `null`, why?
const [todoDetails, setTodoDetails] = useState(null);

// so that you can do something like this
{
  todoDetails && (   // <---------- HERE
    <ul className="list-group">
      <h2>{todoDetails.Title} List</h2>

      {console.log(todoDetails.TodoList)}

      {/* this fails miserably */}
      {todoDetails.TodoList.map(details => (
        <li className="list-group-item" key={details.Id}>
          {details.Title}
        </li>
      ))}
    </ul>
  )
}

Q : Now you are getting multiple console.log,

A : the reason behind this is <React.StrictMode>,

React may invoke render phase lifecycles more than once before committing, or it may invoke them without committing at all (because of an error or a higher priority interruption).

I've removed it from index.js in demo, so you can see the diff


WORKING DEMO :

Edit angry-oskar-37rq9

like image 196
Vivek Doshi Avatar answered Jul 09 '26 07:07

Vivek Doshi