0
const { Button } = require("@material-ui/core");
const { useState } = require("react");

const Test = () => {
  const [loading, setLoading] = useState(false);

  const [todos, setTodos] = useState([]);
  const [users, setUsers] = useState([]);

  const getTodos = async () => {
    setLoading(true);
    const res = await fetch("https://jsonplaceholder.typicode.com/todos");
    const resData = await res.json();
    setTodos(resData);
    setLoading(false);
  };
  const getUsers = async () => {
    setTodos([]);
    setLoading(true);
    console.log(loading)
    const res = await fetch("https://jsonplaceholder.typicode.com/users");
    const resData = await res.json();
    setUsers(resData);
    setLoading(false);
  };

  return (
    <div>
      <Button
        color="primary"
        variant="contained"
        disabled={loading}
        onClick={getTodos}
      >
        Get Todos
      </Button>
      <Button
        color="primary"
        variant="contained"
        disabled={loading}
        onClick={getUsers}
      >
        Get Users
      </Button>
      {todos.map((item) => (
        <div>{JSON.stringify(item)}</div>
      ))}
      {users.map((item) => (
        <div>{JSON.stringify(item)}</div>
      ))}
    </div>
  );
};

export default Test;


Since setLoading is async and does not return any promise, is there any way to successfully set the loading state and then finally fetch the data without using an useEffect with the dependency of loading. I think using an useEffect for the loading state will become complicated because the loading state is used for multiple api calls.

auditore
  • 37
  • 7
  • I don't see how the current implementation would not work. You could `setLoading` before calling `getUsers`? – Baruch Jun 12 '21 at 12:47
  • If we look at `getTodos()` for example, the `setLoading(true)` and the `fetch()` might be called asynchronously instead of one after the other. I actually want it to be synchronous. – auditore Jun 12 '21 at 13:07
  • What you are doing is correct, you only set todo's to `response` and loading to `false` after the fetch, setting state is `async` but shouldn't cause problems in your situation. If you want to set loading after setting the todo's, you will have to use `useEffect`, but your code looks good. – axtck Jun 12 '21 at 13:23
  • so if somehow `setLoading()` takes time, the user will be able to click the button multiple times before the component re-renders. I kind of don't want that to happen. – auditore Jun 12 '21 at 13:38
  • Does this answer your question? [How to use \`setState\` callback on react hooks](https://stackoverflow.com/questions/56247433/how-to-use-setstate-callback-on-react-hooks) – H. Almidan Jun 12 '21 at 14:32

1 Answers1

0

Or you could try to implement like this.

import "./styles.css";

const { Button } = require("@material-ui/core");
const { useState } = require("react");

const Test = () => {
  const [loading, setLoading] = useState(false);

  const [todos, setTodos] = useState([]);
  const [users, setUsers] = useState([]);

  // const getTodos = async () => {
  //   setLoading(true);
  //   const res = await fetch("https://jsonplaceholder.typicode.com/todos");
  //   const resData = await res.json();
  //   setTodos(resData);
  //   setLoading(false);
  // };
  // const getUsers = async () => {
  //   setTodos([]);
  //   setLoading(true);
  //   console.log(loading);
  //   const res = await fetch("https://jsonplaceholder.typicode.com/users");
  //   const resData = await res.json();
  //   setUsers(resData);
  //   setLoading(false);
  // };

  const getTodos = () => {
    setLoading(true);

    const getTodosData = async () => {
      const res = await fetch("https://jsonplaceholder.typicode.com/todos");
      const resData = await res.json();
      setTodos(resData);
      setLoading(false);
    };

    getTodosData();
  };

  const getUsers = () => {
    setTodos([]);
    setLoading(true);
    console.log(loading);

    const getUsersData = async () => {
      const res = await fetch("https://jsonplaceholder.typicode.com/users");
      const resData = await res.json();
      setUsers(resData);
      setLoading(false);
    };

    getUsersData();
  };

  return (
    <div>
      <Button
        color="primary"
        variant="contained"
        disabled={loading}
        onClick={getTodos}
      >
        Get Todos
      </Button>
      <Button
        color="primary"
        variant="contained"
        disabled={loading}
        onClick={getUsers}
      >
        Get Users
      </Button>
      {todos.map((item) => (
        <div>{JSON.stringify(item)}</div>
      ))}
      {users.map((item) => (
        <div>{JSON.stringify(item)}</div>
      ))}
    </div>
  );
};

export default Test;

Tuhin
  • 21
  • 3
  • Will that make any difference though? if we look at `getTodos()` for example, the `setLoading(true)` and `getTodosData()` still might be called asynchronously instead of one after the other – auditore Jun 12 '21 at 13:05
  • @auditore There is no difference, this just delegating it to a different function. There is no problem with your current code. – Baruch Jun 12 '21 at 17:20