title: React widget based api async data fetch - Invalid hook call. Hooks can only be called inside
body: <p>new at React and i am trying to build a widget based dashboard. But i cant get pass this error. No matter what i try and how i try go get the api data for a widget just added to dashboard by the user.</p>
  <p>I wanted to make the Api requests in a separate file &quot;StatsService.js&quot; and fetch data in &quot;Layout.js&quot; when the user adds a new widget to the dashboard and afterwards store the data using redux.</p>
  <p>Heres the StatsService.js file</p>
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-html lang-html prettyprint-override"><code>import  {useEffect,useState} from 'react'
  import { useCookies } from 'react-cookie';
  
  
  async function timeout(ms) {
      return new Promise(resolve =&gt; setTimeout(resolve, ms));
  }
  
  async function WebserviceToken() {
      var myHeaders = new Headers();
  
      myHeaders.append("Content-Type", "application/json");
      myHeaders.append("Origin", "*");
      
      var raw = JSON.stringify({
        "applicationToken": "--------",
        "customerUID": "--------",
        "privateKey": "--------"
      
      });
  
      var requestOptions = {
          method: 'POST',
          headers: myHeaders,
          body: raw,
          redirect: 'follow'
        
        };
  
      const [response, setResponse] = useState(null);
  
      useEffect(() =&gt; {
          (async () =&gt; {
              const result = await fetch(`https://somewebsite.com/webapi/auth/webservicekey`, requestOptions)
              setResponse(result);
          })();
        }, []);
  
        if(response!==null){
          if(response.status=== 200){
              return await response.json();
          }else if(response.status === 429){
              console.log(response.headers["Retry-After"]);
              var RetryDelay = parseInt(response.headers["Retry-After"]) * 1000;
              console.log("Waitt number of miliseconds: "+RetryDelay)
              await timeout(RetryDelay);
              console.log("we waited")
              return await WebserviceToken();
      
          }else{
              console.log(response.status)
              console.log(response)
              alert("Something is very wrong fetching token! - check console")
          }
        }
  }
  
  export async function ApiCall({path,data}){
      let token ="";
      const [cookies, setCookie,removeCookie] = useCookies(['auth2']);
  
  
      if(typeof cookies.auth2 === "undefined"){
          (async () =&gt; {
              const result = await WebserviceToken();
              setCookie('auth2', result.token, { path: '/' });
          })();
      }
  
  
      token = cookies.auth2;
  
      var myHeaders = new Headers();
      myHeaders.append("Origin", "*");
      myHeaders.append("Authorization", "Bearer "+token);
  
      let querystring ="";
      for (const [key, value] of Object.entries(data)) {
          querystring+="&amp;"+key+"="+value;
      }
  
      if (querystring){
          querystring = "?"+querystring.substring(1);
      }
        
      var raw = JSON.stringify(data);
  
      var requestOptions = {
          method: 'GET',
          headers: myHeaders,
          redirect: 'follow'
        };
  
        
      const [response, setResponse] = useState(null);
      const [responseJson, setResponseJson] = useState("");
      useEffect(() =&gt; {
          (async () =&gt; {
              const result = await fetch(`https://somewebsite.com/webapi/${path}${querystring}`, requestOptions)
              setResponse(result);
          })();
        }, []);
  
      if(response!==null){
          if(response.status === 200){
  
              return await response.json();
  
          }else if(response.status === 429){
              var RetryDelay = parseInt(response.headers.get("Retry-After")) * 1000;
  
              console.log("Waitt number of miliseconds: "+RetryDelay)
              await timeout(RetryDelay);
              console.log("we waited")
              
              ApiCall(path, data);
          }else {
              console.log(response.status)
              console.log(response)
              alert("Something is very wrong! - check console")
          }
      }
  
  
      
  }</code></pre>
  </div>
  </div>
  </p>
  <p>And Layout.js</p>
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-html lang-html prettyprint-override"><code>import React, { useState,useEffect, useCallback } from "react";
  import { Responsive as ResponsiveGrid, WidthProvider  } from "react-grid-layout";
  import { withSize } from "react-sizeme";
  import Widget from "./Widget";
  import WidgetModal from "./WidgetModal"
  import  CIcon  from '@coreui/icons-react';
  import { cilApplications} from '@coreui/icons';
  import widgetJson from './WidgetJSON'
  import { useSelector, useDispatch } from 'react-redux'
  import {CButton} from '@coreui/react'
  import {ApiCall} from '../../services/StatsService'
  
  const initialLayouts = {lg:[]};
  const ResponsiveGridLayout = WidthProvider(ResponsiveGrid);
  const Layout = ({ size: { width } }) =&gt;{
    
    const dispatch = useDispatch()
    const leftPanelShow = useSelector((state) =&gt; state.leftPanelShow)
    const widgetData = useSelector((state) =&gt; state.widgetData)
  
    const [widgetModelVisible, setWidgetModelVisible] = useState(false);
  
    const [layouts, setLayouts] = useState(
      getFromLS("layouts") || initialLayouts
    );
  
    const [cols, setCols] = useState(12);
    const [breakpoints, setBreakpoints] = useState({ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 });
    const [droppingItem, setDroppingItem] = useState({ i: "", h: 0, w: 0 });
  
    const [apiData, setApiData] = useState(null);
  
    const onLayoutChange = (_,allLayouts) =&gt; {
       setLayouts(allLayouts);
       onLayoutSave();
    };
  
    const onLayoutSave = () =&gt; {
        saveToLS("layouts", layouts);
        dispatch({ type: 'set', layouts: layouts });
    };
  
    const onRemoveItem = (itemId,event) =&gt; {
        setLayouts({
          lg:[...layouts.lg.filter(item=&gt;item.i!==itemId)]
        });  
  
        onLayoutSave();
  
        return false;
    };
  
    const onAddItem = (item) =&gt; {
  
      var tempLayouts = {...layouts};
      var lg = {...item.gridLayout};
      if(typeof tempLayouts.lg === "undefined"){
        tempLayouts.lg = [];
      }
  
      let nextXPos = 0;
      let nextYPos = 0;
      let totalWH = {width:0,height:0,onlyHighestItemsInRow:0,highestItemInRow:0} 
      tempLayouts.lg.map(function(layout, i){
        totalWH.height += layout.h;
        totalWH.width += layout.w;
        if((totalWH.width % cols) === 0 || (tempLayouts.lg.length-1) === i){
          totalWH.onlyHighestItemsInRow += totalWH.highestItemInRow;
          totalWH.highestItemsInRow = 0;
        }else{
          if(layout.h &gt; totalWH.highestItemInRow){
            totalWH.highestItemInRow = layout.h;
          }
        }
      })
      nextXPos = (totalWH.width) % cols;
      if(nextXPos!== 0 &amp;&amp; nextXPos &gt; (cols/2)){
        nextXPos = 0;
      }
  
      nextYPos =totalWH.onlyHighestItemsInRow;
  
      lg.i =item.id;
      lg.x = nextXPos;
      lg.y = nextYPos;
  
      tempLayouts.lg.push(lg)
      setLayouts(tempLayouts);
      onLayoutSave();
    };
  
    const ResetLayout = () =&gt; {
      localStorage.clear() 
      setLayouts(initialLayouts);
    }
  
    const onWidgetModalCloseClick = () =&gt; {
      setWidgetModelVisible(false)
    }
  
    const onBreakpointChange = (breakpoint, cols) =&gt;{
      setCols(cols)
      setBreakpoints(breakpoints)
    }
  
    const onResize = (l, oldLayoutItem, layoutItem, placeholder) =&gt; {
  
      const heightDiff = layoutItem.h - oldLayoutItem.h;
        const widthDiff = layoutItem.w - oldLayoutItem.w;
        const changeCoef = oldLayoutItem.w / oldLayoutItem.h;
        if (Math.abs(heightDiff) &lt; Math.abs(widthDiff)) {
          layoutItem.h = layoutItem.w / changeCoef;
          placeholder.h = layoutItem.w / changeCoef;
        } else {
          layoutItem.w = layoutItem.h * changeCoef;
          placeholder.w = layoutItem.h * changeCoef;
        }
    }
  
    const onDrop = async (layout, layoutItem, _event) =&gt; {
      setTimeout(() =&gt; {
          var tempLayouts = {...layouts};
          setLayouts(tempLayouts);
          dispatch({ type: 'set', layouts: tempLayouts })
      }, 100);
    }
  
    const onDragStart = (item) =&gt; {
      setDroppingItem({i:item.id,h:item.gridLayout.h,w:item.gridLayout.w})
    }
  
    useEffect(() =&gt; {
  
      dispatch({ type: 'set', layouts: layouts })
      dispatch({ type: 'set', onDragStart: onDragStart })
      dispatch({ type: 'set', widgetData: widgetData })
  
    }, [dispatch]);
    
  
    useEffect(() =&gt; {
  
      layouts.lg.map((item,i) =&gt;{
        var livedata = widgetData.filter(w =&gt;w.id === item.i);
        if(livedata.length!==0){
          item.dataFromApi = livedata[0].data;
        }else{
  
          console.log(item);
          const wJson = widgetJson.filter(w=&gt;w.id === item.i)[0]; 
          console.log(wJson);
          let data = null;
  
          (async () =&gt; {
            setApiData(await ApiCall({path:wJson.liveData.path,data:wJson.liveData.defaultValues}));
          })();
  
          data = apiData;
           
          var tempWidgetData = [...widgetData]
  
          console.log(tempWidgetData);
  
          tempWidgetData.push({id:item.id, data:data});
          dispatch({ type: 'set', widgetData: tempWidgetData })
        }
  
      })
  
    }, [layouts]);
    
  
    return (
      &lt;&gt;
      &lt;div className="gridlayout d-grid gap-2 d-md-flex justify-content-md-end"&gt;
        &lt;CButton color="primary" onClick={() =&gt;  dispatch({ type: 'set', leftPanelShow: leftPanelShow?false:true })} className="me-md-2"&gt;
          &lt;CIcon icon={cilApplications} className="me-2" /&gt; Widget panel
        &lt;/CButton&gt;
        &lt;CButton color="secondary" onClick={ResetLayout}&gt;
          Reset
        &lt;/CButton&gt;
      &lt;/div&gt;
        &lt;ResponsiveGridLayout
          className="layout"
          layouts={layouts}
          autoSize={true}
          breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
          cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
          rowHeight={60}
          width={width}
          onLayoutChange={onLayoutChange}
          onBreakpointChange={onBreakpointChange}
          onResize={onResize}
          isDroppable={true}
          onDrop={onDrop}
          droppingItem={droppingItem}
          style={{minHeight: '500px'}}
        &gt;
        {layouts.lg.map((item, i) =&gt; (
                &lt;div
                  key={item.i}
                  className="card"
                &gt;
                    &lt;Widget key={item.i} onRemoveItem={onRemoveItem} chart={widgetJson.filter(w=&gt;w.id === item.i)[0]} /&gt; 
  
                &lt;/div&gt;
              ))
            
       }
        &lt;/ResponsiveGridLayout&gt;     
      &lt;/&gt;
    );
  }
  
  export default withSize({ refreshMode: "debounce", refreshRate: 60 })(Layout);
  
  function getFromLS(key) {
    let ls = {};
    if (global.localStorage) {
      try {
        ls = JSON.parse(global.localStorage.getItem("rgl-8")) || {};
      } catch (e) {}
    }
    return ls[key];
  }
  
  function saveToLS(key, value) {
    if (global.localStorage) {
      global.localStorage.setItem(
        "rgl-8",
        JSON.stringify({
          [key]: value
        })
      );
    }
  }</code></pre>
  </div>
  </div>
  </p>
  <p>The error is at UseEffect, where i loop trough all the items in <strong>layouts</strong>, this is where i try to use either data stored with redux of fetching the api data, if data doesn't exist.</p>