Direct 96
Direct 96

Reputation: 137

Multiple fetch data axios with React Hooks

I would like to get global information from Github user and his repos(and get pinned repos will be awesome). I try to make it with async await but It's is correct? I've got 4 times reRender (4 times console log). It is possible to wait all component to reRender when all data is fetched?

function App() {
  const [data, setData] = useState(null);
  const [repos, setRepos] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      const respGlobal = await axios(`https://api.github.com/users/${username}`);
      const respRepos = await axios(`https://api.github.com/users/${username}/repos`);

      setData(respGlobal.data);
      setRepos(respRepos.data);
    };

    fetchData()

  }, []);

  if (data) {
    console.log(data, repos);
  }

  return (<h1>Hello</h1>)
}

Upvotes: 12

Views: 36642

Answers (4)

Shubham Khatri
Shubham Khatri

Reputation: 281646

Multiple state updates are batched but but only if it occurs from within event handlers synchronously and not setTimeouts or async-await wrapped methods.

This behavior is similar to classes and since in your case its performing two state update cycles due to two state update calls happening

So Initially you have an initial render and then you have two state updates which is why component renders three times.

Since the two states in your case are related, you can create an object and update them together like this:

function App() {
  const [resp, setGitData] = useState({ data: null, repos: null });

  useEffect(() => {
    const fetchData = async () => {
      const respGlobal = await axios(
        `https://api.github.com/users/${username}`
      );
      const respRepos = await axios(
        `https://api.github.com/users/${username}/repos`
      );

      setGitData({ data: respGlobal.data, repos: respGlobal.data });
    };

    fetchData();
  }, []);

  console.log('render');
  if (resp.data) {
    console.log("d", resp.data, resp.repos);
  }

  return <h1>Hello</h1>;
}

Working demo

Upvotes: 22

Dmitriy Mozgovoy
Dmitriy Mozgovoy

Reputation: 1597

For other researchers (Live demo):

import React, { useEffect, useState } from "react";
import { CPromise, CanceledError } from "c-promise2";
import cpAxios from "cp-axios";

function MyComponent(props) {
  const [error, setError] = useState("");
  const [data, setData] = useState(null);
  const [repos, setRepos] = useState(null);

  useEffect(() => {
    console.log("mount");
    const promise = CPromise.from(function* () {
      try {
        console.log("fetch");
        const [respGlobal, respRepos] = [
          yield cpAxios(`https://api.github.com/users/${props.username}`),
          yield cpAxios(`https://api.github.com/users/${props.username}/repos`)
        ];

        setData(respGlobal.data);
        setRepos(respRepos.data);
      } catch (err) {
        console.warn(err);
        CanceledError.rethrow(err); //passthrough
        // handle other errors than CanceledError
        setError(err + "");
      }
    }, []);

    return () => {
      console.log("unmount");
      promise.cancel();
    };
  }, [props.username]);

  return (
    <div>
      {error ? (
        <span>{error}</span>
      ) : (
        <ul>
          <li>{JSON.stringify(data)}</li>
          <li>{JSON.stringify(repos)}</li>
        </ul>
      )}
    </div>
  );
}

Upvotes: 0

Andrew
Andrew

Reputation: 106

Figured I'd take a stab at it because the above answer is nice, however, I like cleanliness.

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

const Test = () => {
    const [data, setData] = useState([])

    useEffect(() => {
        (async () => {
            const data1 = await axios.get('https://jsonplaceholder.typicode.com/todos/1')
            const data2 = await axios.get('https://jsonplaceholder.typicode.com/todos/2')
            setData({data1, data2})
        })()
    }, [])

    return JSON.stringify(data)
}

export default Test

Using a self invoking function takes out the extra step of calling the function in useEffect which can sometimes throw Promise errors in IDEs like WebStorm and PHPStorm.

Upvotes: 1

Zaid Akhter
Zaid Akhter

Reputation: 11

function App() {
  const [resp, setGitData] = useState({ data: null, repos: null });

  useEffect(() => {
    const fetchData = async () => {
      const respGlobal = await axios(
        `https://api.github.com/users/${username}`
      );
      const respRepos = await axios(
        `https://api.github.com/users/${username}/repos`
      );

      setGitData({ data: respGlobal.data, repos: respGlobal.data });
    };

    fetchData();
  }, []);

  console.log('render');
  if (resp.data) {
    console.log("d", resp.data, resp.repos);
  }

  return <h1>Hello</h1>;
}

he made some mistake here:
setGitData({ data: respGlobal.data, repos: respGlobal.data(respRepos.data //it should be respRepos.data});

Upvotes: 0

Related Questions