React - call api and export value

Viewed 835

I'm learning react and I took template as an example, I would like change menu values. The menus are create with fixed values, and I need to access my API and get values from the database, but when I try do that, I get an error return:

const LoadNavigation = async () => {
    return await axios.get('https://localhost:44362/api/SystemMenus').then(response => response.data);
}

var nav = LoadNavigation();

export const navigations = nav;

Error:

TypeError: Invalid attempt to spread non-iterable instance. In order to be iterable, non-array objects must have a Symbol.iterator method.

If I pass the fixed export value (json) it works:

export const navigations = [
    {
        name: 'Home',
        path: '/dashboard',
        icon: 'home',
    },
    {
        label: 'Dashboards',
        type: 'label',
    },
    {
        name: 'Test 1',
        icon: 'trending_up',
        children: [
            {
                name: 'Test 02',
                iconText: 'CP',
                path: '/test/test02',
            },
            {
                name: 'Test 03',
                iconText: 'FC',
                path: '/test/test03',
            },
        ],
    },
    
]

Another point is that my API is called after my const has been exported.

Promise

How do I solve these two problems, I couldn't find anything similar or that I could understand.

I did a lot of testing and modifying the code, but I can't get it to work.

EDIT - Answer @Giovanni Esposito

In this case it occurs and calls API

import axios from 'axios';

navigationsAPI.js

export const LoadNavigation = async () => {
    let response = await axios.get('https://localhost:44362/api/SystemMenus');
    return response.data;
}

navigation.js

import React from 'react';
import { LoadNavigation } from 'app/navigationsAPI'

var nav = LoadNavigation();

export const navigations = nav;

Error:

TypeError: Invalid attempt to spread non-iterable instance. In order to be iterable, non-array objects must have a Symbol.iterator method.

Error3

EDIT - Answer @Drew Reese

In this case only the error occurs, it does not call API

navigationsAPI.js

import axios from 'axios';

export const LoadNavigation = () => {
    return axios.get('https://localhost:44362/api/SystemMenus')
        .then(response => response.data);
}

navigation.js

import React from 'react';
import { LoadNavigation } from 'app/navigationsAPI'

const [nav, setNav] = React.useState([]);

React.useEffect(() => {
    LoadNavigation().then(setNav);
}, []);


export const navigations = nav;

Error:

Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:

  1. You might have mismatching versions of React and the renderer (such as React DOM)
  2. You might be breaking the Rules of Hooks
  3. You might have more than one copy of React in the same app

Erro2

2 Answers

Unless your system and browser supports top-level await (or similar behavior) this isn't possible to make the code wait for the response & handling before the navigations is exported. This probably isn't what you want to do anyway.

To handle this normally you could export the LoadNavigation function, and use a local navigations state variable and mounting effect to fetch and load the menu data.

export const LoadNavigation = () => {
  return axios.get('https://localhost:44362/api/SystemMenus')
    .then(response => response.data);
}

In consuming component;

const MyComponent = () => {

  const [navigations, setNavigations] = React.useState([]);

  React.useEffect(() => {
    LoadNavigation().then(setNavigations); // *
  }, []);

  ...

}

* Note: Here LoadNavigation().then(setNavigations); is passing the setNavigations function as the .then callback, but if it's more clear you can use an anonymous callback and proxy the response data.

React.useEffect(() => {
  LoadNavigation().then(data => setNavigations(data));
}, []);

You could adopt also this way:

navigationsAPI.js

import axios from 'axios';   

export const LoadNavigation = async () => {  
  let response = await axios.get('https://localhost:44362/api/SystemMenus');
  return response.data;
}

In any component in which you need to load navigation:

import React, { useState, useEffect } from 'react';
import { LoadNavigation } from './navigationsAPI'

const Component = () => {
   const [navigation, setNavigation] = useState();

   useEffect(() => {
      LoadNavigation().then(response => setNavigation(response));
      
   }, []);

   return (...);
}
Related