skip to Main Content

I’ve made a dashboard in React. It has no active updating, no buttons, fields or drop-downs. It will be deployed on a wall TV for viewing. All panels (9 total) are updated through the API call. The initial call (seen below) works, and all JSON data is fetched and the dashboard is initially updated.

BOTTOM LINE PROBLEM: I need to call the API every 30 sec to 1 minute after the initial call to check for updates.

I have attempted “setInterval” inside the componentDidMount() as suggested by people on here answering others’ questions and I get an error “await is a reserved word”. I’ve read about forceUpdate() which seems logical for my use case given what the facebook/react page says about it. But, I’ve read on here as well to stay away from that…

The code below is a light version of the code I’m using. I’ve removed many of the components and imports for brevity’s sake. Any help would be greatly appreciated.

import React, { Component } from 'react';

import Panelone from './Components/Panelone';
import Paneltwo from './Components/Paneltwo';

class App extends Component {

  state = {
      panelone: [],
      paneltwo: []
    }

 async componentDidMount() {
      try {
        const res = await fetch('https://api.apijson.com/...');
        const blocks = await res.json();
        const dataPanelone = blocks.panelone;
        const dataPaneltwo = blocks.paneltwo;

        this.setState({
          panelone: dataPanelone,
          paneltwo: dataPaneltwo,
        })
      } catch(e) {
        console.log(e);
      }

  render () {
    return (
      <div className="App">
        <div className="wrapper">
          <Panelone panelone={this.state} />
          <Paneltwo paneltwo={this.state} />
        </div>
      </div>
    );
  }
}

export default App;

7

Answers


  1. Move the data fetch logic into a seperate function and invoke that function using setInterval in componentDidMount method as shown below.

      componentDidMount() {
        this.loadData()
        setInterval(this.loadData, 30000);
      }
    
      async loadData() {
         try {
            const res = await fetch('https://api.apijson.com/...');
            const blocks = await res.json();
            const dataPanelone = blocks.panelone;
            const dataPaneltwo = blocks.paneltwo;
    
            this.setState({
               panelone: dataPanelone,
               paneltwo: dataPaneltwo,
            })
        } catch (e) {
            console.log(e);
        }
      }
    

    Below is a working example

    https://codesandbox.io/s/qvzj6005w

    Login or Signup to reply.
  2. In order to use await, the function directly enclosing it needs to be async. According to you if you want to use setInterval inside componentDidMount, adding async to the inner function will solve the issue. Here is the code,

     async componentDidMount() {
              try {
                setInterval(async () => {
                  const res = await fetch('https://api.apijson.com/...');
                  const blocks = await res.json();
                  const dataPanelone = blocks.panelone;
                  const dataPaneltwo = blocks.paneltwo;
    
                  this.setState({
                    panelone: dataPanelone,
                    paneltwo: dataPaneltwo,
                  })
                }, 30000);
              } catch(e) {
                console.log(e);
              }
        }
    

    Also instead of using setInterval globally, you should consider using react-timer-mixin. https://facebook.github.io/react-native/docs/timers.html#timermixin

    Login or Signup to reply.
  3. I figured I’d chime in with a slightly revised approach that uses recursion via a setTimeout call within the function block. Works the same…maybe slightly cleaner to have the function call itself from within, instead of doing this elsewhere in your code?

    This article explains the reasoning in a bit more depth…but I’ve been using this approach for several dashboards at work – does the job!

    Would look something like this:

    class MyComponent extends React.Component
    //create the instance for your interval
    intervalID;
    constructor(props) {
        super(props);
        this.state = {
            data: [],
            loading: false,
            loadingMap: false,
    
            //call in didMount...
            componentDidMount() {
              this.getTheData()
            }
    
     getTheData() {
     //set a loading state - good practice so you add a loading spinner or something
       this.setState({loading: true}), () => {
     //call an anonymous function and do your data fetching, then your setState for the data, and set loading back to false
       this.setState({
           data: fetchedData,
           loading: false
           )}     }
     //Then call the function again with setTimeout, it will keep running at the specified //interval...5 minutes in this case
                this.intervalID = setTimeout(
                  this.getTheData.bind(this),
                  300000
                );
    
              }
            }
            //Important! Be sure to clear the interval when the component unmounts! Your app might crash without this, or create memory leaks!
            componentWillUnmount() {
              clearTimeout(this.intervalID);
            }

    Sorry if the formatting got a little off. Haven’t tried this with Hooks yet but I think you’d have a similar implementation in a useEffect call? Has anyone done that yet?

    Login or Signup to reply.
  4. I have seen around a lot of complications about this. No need to have it in the lifecycles or in state or promisses.
    In here, the service api is just a simple axios api call

    This is my full implementation as I use it with context api(omitting some private code).
    In my case I just care about the status response in the api since I know what I need to change. But the api can be really anything you need for/from data-wise.’

        export class MyContextApiComponent ..... {
        private timeout: ReturnType<typeof setInterval> | undefined
        ...
        ...
        ...
        public statsPolling = (S_UUID: string) => {
            if (!this.timeout) {
                this.timeout = setInterval( () => { 
                    this.statsPolling(S_UUID)
                }, 3000)
            }
    
            this.state.api.StatisticsService.statsPolling(S_UUID)
                .then(res => {
                    if (res.hasDescStats) {
                        clearInterval(this.timeout)
                        
                        this.setState(prevState => ({
                            ...prevState,
                            ...
                            ...
                        }))
                    }
                })
                .catch(e => console.warn('', e))
        }
       ...
       ...
    }
    
    /// in another file in service is the api call itself with axios just checking on the server reply status
    
    export class Statistics implements IStatistics {
        public statsPolling: StatsPolling = async S_UUID => {
            return axios
                .get<{ hasDescStats: boolean }>(`/v2/api/polling?query=${S_UUID}`)
                .then(res => {
                    if (res.status === 200) {
                        return { hasDescStats: true }
                    } else {
                        return { hasDescStats: false }
                    }
                })
        }
    }
    
    Login or Signup to reply.
  5. Answer

    You can create a function for the componentDidMount code.

    import React, { Component } from 'react';
    
    import Panelone from './Components/Panelone';
    import Paneltwo from './Components/Paneltwo';
    
    class App extends Component {
    
      state = {
          panelone: [],
          paneltwo: []
        }
    code = async () => {
    try {
            const res = await fetch('https://api.apijson.com/...');
            const blocks = await res.json();
            const dataPanelone = blocks.panelone;
            const dataPaneltwo = blocks.paneltwo;
    
            this.setState({
              panelone: dataPanelone,
              paneltwo: dataPaneltwo,
            })
          } catch(e) {
            console.log(e);
          }
    }
    
     componentDidMount() {
        
          }
    
      render () {
        return (
          <div className="App">
            <div className="wrapper">
              <Panelone panelone={this.state} />
              <Paneltwo paneltwo={this.state} />
            </div>
          </div>
        );
      }
    }
    
    export default App;
    

    then make a componentDidUpdate

    import React, { Component } from 'react';
    
    import Panelone from './Components/Panelone';
    import Paneltwo from './Components/Paneltwo';
    
    class App extends Component {
    
      state = {
          panelone: [],
          paneltwo: []
        }
    code = async () => {
    try {
            const res = await fetch('https://api.apijson.com/...');
            const blocks = await res.json();
            const dataPanelone = blocks.panelone;
            const dataPaneltwo = blocks.paneltwo;
    
            this.setState({
              panelone: dataPanelone,
              paneltwo: dataPaneltwo,
            })
          } catch(e) {
            console.log(e);
          }
    }
    
     componentDidMount() {
        this.code()    
     }
     componentDidUpdate(){
    this.code()
    }
      render () {
        return (
          <div className="App">
            <div className="wrapper">
              <Panelone panelone={this.state} />
              <Paneltwo paneltwo={this.state} />
            </div>
          </div>
        );
      }
    }
    
    export default App;
    
    Login or Signup to reply.
  6. For those looking for functional components. You can update the state every n time by creating a setInterval and calling this in the useEffect hook. Finally call the clearInterval method in the clean up function

    import React, { useEffect, useState } from "react";
    
    import Panelone from "./Components/Panelone";
    import Paneltwo from "./Components/Paneltwo";
    
    function App() {
      const [state, setState] = useState({
        panelone: [],
        paneltwo: [],
      });
    
      const getData = async () => {
        try {
          const res = await fetch("https://api.apijson.com/...");
          const blocks = await res.json();
          const dataPanelone = blocks.panelone;
          const dataPaneltwo = blocks.paneltwo;
    
          setState({
            panelone: dataPanelone,
            paneltwo: dataPaneltwo,
          });
        } catch (e) {
          console.log(e);
        }
      };
    
      useEffect(() => {
        const intervalCall = setInterval(() => {
          getData();
        }, 30000);
        return () => {
          // clean up
          clearInterval(intervalCall);
        };
      }, []);
      return (
        <div className="App">
          <div className="wrapper">
            <Panelone panelone={state} />
            <Paneltwo paneltwo={state} />
          </div>
        </div>
      );
    }
    
    export default App;
    
    Login or Signup to reply.
  7. You also need to clear this interval, else it will keep on calling the API. To clear the interval, you need to have a reference and then you can clear it on ComponentWillUnmount.
    You can use useRef hook with your class component for this purpose.

    class Test extends React.Component {
      constructor() {
        super(props);
        this.selectorRef = React.createRef(null);
       }
    
       componentDidMount() {
         this.selectorRef.current = setInterval(async () => {
         
           *// your function here*
    
         }, 30000);
    
       }
    
      componentWillUnmount() {
       clearInterval(this.selectorRef.current);
      }
    }

    Functional Component Approach

    const intervalRef: { current: NodeJS.Timeout | null } = useRef(null);
    
    useEffect(() => {
      intervalRef.current = setInterval(async () => {
      
      /* your function here. 
      ** call your api here
      */
      
      }, 30000);
      
      return () => {
          clearInterval(intervalRef.current as NodeJS.Timeout);
        }
    }, []);
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search