Search code examples
reactjsreact-hooksaxiosuse-effect

UseEffect & Axios Get issues on loading data to Card


Working with a simple card component, to which I load data from an API with Axios get by using UseEffect. The purpose is that when the component is loaded, it loads the data to it once.

The issue is that data is not loaded correctly on load but repeatedly after it when the component updates. How can I make it so that it loads the data once when the page is loaded but not after that?

I have omitted the API url but it is tested and returns data correctly from backend.

Card component:

import React from 'react';
import { Card, CardImg, CardBody, CardTitle, CardSubtitle, Container, Row, Col } from 'reactstrap';

import { useEffect, useState } from "react";
import { APIGet } from '../API.js';


function RenderCardLoadMore() {
    const [data, setData] = useState([]);

    const resource = 'search';
    const params = { 'limit': '2' };
    const results = APIGet(resource, params);

    useEffect(() => {
        console.log("initialload");
        setData(results);
    }, []);

    return (
        <Container fluid>
            <Container>
                <Row>
                    {data.map((v, i) => (
                        <Col className="py-2" key={i} xs={12} md={4} lg={3}>
                            <Card className="explore_event_card shadow">
                                <CardBody>
                                    <CardTitle className="card_header">{v.Title}</CardTitle>
                                    <CardSubtitle>{v.SubTitle}</CardSubtitle>
                                </CardBody>
                            </Card>
                        </Col>
                    ))}
                </Row>
            </Container>
        </Container>
    );
}

export default RenderCardLoadMore;

API component:

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

const API_URL = 'https://myapi/'; /*url omitted for this question, tested and working*/

const session_id = localStorage.getItem("session_id");

export function APIGet (resource, params) {
    const [data, setData] = useState([]);
    const url = API_URL + resource;

    params['session_id'] = session_id;

    useEffect(() => {
        axios.get(url, {params: params}).then((v) => {
            setData(v.data)
        }).catch( (err) => console.log(["APIGet error:", err]) )
    }, [url]); //url as 2nd argument not needed?

    return data;
}

Solution

  • You could remove url and use an empty dependency array so the useEffect hook is triggered only once after the initial render. From what I can tell though, APIGet doesn't need to be a hook and doesn't need to use the useState hook. I can simply return the Promise chain returned from axios.

    API

    import axios from 'axios';
    
    const API_URL = 'https://myapi/.....';
    
    export function APIGet (resource = "", params = {}) {
      const session_id = JSON.parse(localStorage.getItem("session_id"));
      const url = API_URL + resource;
    
      params['session_id'] = session_id;
    
      return axios.get(url, {params: params})
        .then((v) => {
          return v.data
        })
        .catch((err) => console.log(["APIGet error:", err]));
    }
    

    RenderCardLoadMore - Call APIGet in the useEffect hook and update the state when Promise resolves.

    import { APIGet } from '../API.js';
    
    function RenderCardLoadMore() {
      const [data, setData] = useState([]);
    
      const resource = 'search';
      const params = { 'limit': '2' };
    
      useEffect(() => {
        console.log("initialload");
        APIGet(resource, params)
          .then(results => {
            setData(results);
          });
      }, []);
    
      return (....);
    }