0

I have an issue which I'm beginning to suspect has no solution unless I drop React and return to jQuery. I want to create an app that is similar to https://tenno.tools/ or https://deathsnacks.com/wf/ These are sites which grab JSON data and update periodically.

I want to make a react app that uses axios to refresh the data once per minute with setTimeout, since the data changes often.

  axiosFunc = () => {

    axios.get('https://api.warframestat.us/pc').then(results => {
      this.setState({
        alerts: results.data.alerts
      });

      setTimeout(this.axiosFunc,1000 * 60);
    })
  }
  componentDidMount() {
    this.axiosFunc();
  }

Next I need to use map to cycle through the alert array's objects and make individual components based off the objects' data that are active.

render() {

    return (
      <main className="content">
        <header>{this.state.whichEvent.toUpperCase()}</header>

        {this.state.alerts.map(alert => {
          //Variables that pull time based data from the objects go here, and go into the component as props 
            <AlertsBox key={alert.id}/>
        })}
      </main>
    );
  }

Then I use the props and state within the component to make a timer, since the data from the JSON file have expiration dates...

let timer = () => {
      //Extract the data from the original string
      //Convert the UTC to locale time

      let seconds = Math.round((this.state.eta/1000) % 60);
      let minutes = Math.floor( (this.state.eta/1000/60) % 60 );
      let hours = Math.floor( (this.state.eta/(1000*60*60)) % 24 );
      let days = Math.floor( this.state.eta/(1000*60*60*24) );

      return `${days >= 1? days + " days" : ""} ${hours >= 1? hours + " hrs" : ""} ${minutes} min ${seconds} sec`
    }

And all of this works. I'm able to see the dynamic data from the JSON as they come in and leave, as well as the corresponding time. Now I just need to use setInterval in order to get the timer to tick every second. Is this possible? I asked a similar question here

How can I return values once per second using react and axios?

But again, I'm beginning to suspect that this isn't actually possible. Is it?

Matiny
  • 179
  • 3
  • 13

2 Answers2

0

You'll want to use setInterval on the axiosFunc, no need to set that up inside the network request. Here's an example that calls your API every 5 seconds and renders a formatted date.

class Example extends React.Component {
  constructor() {
    super();
    this.state = { alerts: [] };
  }

  axiosFunc = () => {
    axios.get('https://api.warframestat.us/pc').then(results => {
      this.setState({
        alerts: results.data.alerts,
      });

      console.log('Updated the data!', results);
    });
  };

  timer = time => {
    // Your timer code goes here, just printing out example data here.
    const date = new Date(time);
    return `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;
  };

  componentDidMount() {
    this.axiosFunc();
    this.interval = setInterval(this.axiosFunc, 5000);
  }

  componentWillUnmount() {
    clearInterval(this.interval);
  }

  render() {
    if (!this.state.alerts.length) {
      return <div />;
    }

    // Sorting the alerts every time we render.
    const latest = this.state.alerts.sort((a, b) => {
      return new Date(b.activation) - new Date(a.activation);
    })[0];

    return <div>{this.timer(latest.activation)}</div>;
  }
}

ReactDOM.render(<Example />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.17.1/axios.min.js"></script>

<div id="root"></div>
Fabian Schultz
  • 18,138
  • 5
  • 49
  • 56
  • Hello, considering that the source JSON data changes by itself (hence auto updating), how do I keep up with the code? Here, you hard coded the index: return
    {this.timer(this.state.alerts[0].activation)}
    , but how do I keep it dynamic?
    – Matiny Jan 21 '18 at 22:52
  • Every time new data is fetched, `render` is called again and you can react to those data changes dynamically. I added a sorting mechanism in the example, but you could do practically anything in there. – Fabian Schultz Jan 21 '18 at 23:04
  • Just wanted to add, that If you use `setTimeout` you separate the end of one and the beginning of the next API call with a timeout. When using `setInterval` as in your example, you just call the API each x time units and don't care how long the call is gonna take. Sometimes a bit problematic when setting an interval of 5 seconds and your calls are taking longer than that. – Michael Faisst Apr 21 '19 at 17:16
-1

It's definitely possible. As you said, all of this works - which part is actually giving you trouble? Are you getting an error anywhere?

Personally, I'd think about using Redux in addition to React in an app like this because I like to separate the fetching of data from the presentation of data, but that's all just personal preference. I have an example app that uses setInterval directly in a React component, in case the move from setTimeout to setInterval is causing you pain.

carpeliam
  • 6,691
  • 2
  • 38
  • 42
  • The problem is that I have no idea how to update the timer function with setInterval. It gives me a set of numbers like 7, 8, and 9 instead of a timer. I tried updating the state from within the component, which causes more errors. – Matiny Jan 21 '18 at 23:01
  • @Matiny I'm guessing the set of numbers you're referring to is the return value of `setInterval`? The return value of `setInterval` is the interval ID - when you're ready to stop listening (which you probably want to do when your component unloads, as in the example), you can give that interval ID to `clearInterval`. – carpeliam Jan 22 '18 at 00:39