Ryan S
Ryan S

Reputation: 155

Make ReactJS render after Axios request is completed

I'm using an axios request to trigger my backend (expressJS) to make a request to my Heroku Postgres database ('/getImageData'), which can be seen here in my Annotator.js file.

    const getImageData = async (id) => {
    try {
        return await axios.get('/getImageData', {
            params: {
                id: id
            }
        }
        )
    }
    catch (err) {
        console.error(err);
    }
}

In the main function of this Annotator.js file, I'm trying to call this method to get the data and return it in the ReactImageAnnotate component as seen here:

function AgileAnnotator() {
    let content = false;
    let data = null;
    const { id } = useParams();

    const imageData = getImageData(id)
        .then(response => {
            if (response != undefined && response != null) {
                const row = response.data.rows[0];
                console.log(row);
                console.log(JSON.stringify(row));
                content = true;
                data = (
                    <div>
                        <ReactImageAnnotate
                            selectedImage={row.selectedimage}
                            taskDescription={row.taskdescription}
                            images={row.images}
                            regionClsList={row.regionclslist}
                            enabledTools={row.enabledtools}
                            onExit={onExit}
                        />
                    </div>
                )
            }
            else {
                data = (
                    <div>
                        <h1>Error.</h1>
                    </div>
                )
            }

        })
        .catch(error => {
            console.error(error);
            data = (
                <div>
                    <h1>Error.</h1>
                </div>
            )
        })
    
    if (data == null) {
        console.log("Returning null");
        return null;
    }
    return ({ data });
}
export default AgileAnnotator;

The issue is that it's returning null, instead of the actual data which I'm assuming is because it's rendering before Axios can fetch the data.

Here is my main App.js file which uses this Annotator.js file:

import React from 'react';
import './App.css';
import {
  BrowserRouter as Router,
  Switch,
  Route
} from 'react-router-dom'

import AgileAnnotator from './components/Annotator';

    
    const App = () => (
      <Router>
        <Switch>
          <Route path="/:id">
            <AgileAnnotator />
          </Route>
        </Switch>
      </Router>
    )
    export default App;

It shows in the console that the data is being fetched, but I can also see that it's returning null as seen here whenever I go to the at x.x.x/2:

Console from webpage @ x.x.x/2

I've just tried just returning the 's at each point in the code rather than setting them to the data variable.

So in conclusion, I'm trying to render the ReactImageAnnotate element once Axios has finished fetching the data. I'm quite new to ReactJS so sorry if this is something easy!

Thanks very much!

Upvotes: 0

Views: 3228

Answers (2)

Tarukami
Tarukami

Reputation: 1160

the idea is to use two hooks:

  1. useState to connect your representing data component with actual data
  2. useEffect for getting data from server and updating data.

The component receive data as props. The data changes = the component updates.

The template is like this:

const App = () => {
  const [data, setData] = useState(null) //initial state is null
  
  useEffect(()=>{
    const url = 'https://yoururl.com'
    axios(url).then(({data}) => {
       setData(data)
    })
  },[]) // empty array makes hook working once

  return data && <ComponentRepresentingData data={data} /> // best place to show loading process or the data itself when received

Upvotes: 3

Elias Schablowski
Elias Schablowski

Reputation: 2812

Changing variables does not cause rerendering of components in react, to rerender a component the state must change or the rerender must be forced - furthermore, varibles defined with var/let/const in a context (in this case a function) will always be overwritten when the context is recreated.

Since you are using react function components, you should utilize the useState hook to re-render your component once the request is completed and useEffect to ensure you only request once, rather than on every re-render.

import { useEffect, useState } from "react";
function AgileAnnotator() {
    let content = false;
    const { id } = useParams();
    const [ imageData, setImageData ] = useState(undefined);

    useEffect(()=> getImageData(id)
        .then(response => {
            if (response != undefined && response != null) {
                const row = response.data.rows[0];
                console.log(row);
                console.log(JSON.stringify(row));
                content = true;
                setImageData(row);
            }
            else {
                setImageData(new Error("response is null/undefined"));
            }

        })
        .catch(error => {
            console.error(error);
            setImageData(error);
        }), [id]);
    
    if (imageData == null) {
        console.log("Returning null");
        return null; // This should really return a placeholder / spinner, though opt for the former
    }
    if (imageData instanceof Error) {
        console.error(imageData);
        return (
                    <div>
                        <h1>Error.</h1>
                    </div>
                );
    }
    return (
                <div>
                    <ReactImageAnnotate
                          selectedImage={imageData.selectedimage}
                          taskDescription={imageData.taskdescription}
                          images={imageData.images}
                          regionClsList={imageData.regionclslist}
                          enabledTools={imageData.enabledtools}
                          onExit={onExit}
                    />
                </div>
            );
}
export default AgileAnnotator;

Upvotes: 1

Related Questions