DEV Community

loading...

Fetching data with React the easy way

artydev profile image artydev ・1 min read

I don't use React in my day to day work. When I see articles on it, I'm always surprised how simple things are over complicated

The following example uses several techniques wich simplify state management, but requires explanations.
Feel free to post questions.


import React, { useState } from 'react';

const timeOut = 1000;
let request = "https://randomuser.me/api/?results=10";
let response = null;

const fetchUsers = async function (actions) {
  actions.setLoading(true);
  try {
    response = await Promise.race([
      fetch(request),
      new Promise((res, reject) =>
        setTimeout(() => reject(new Error("Timeout")), timeOut))
    ]);
    var data = await response.json();
    actions.loadData(data.results);
  }
  catch (e) {
    actions.setLoading(false);
    alert(e.message);
  }
}

const listUsers = function (data) {
  return (
    <ul>{data.map((d, i) =>
      <li key={i}>
        <img src={d.picture.thumbnail} alt="user" style={{ margin: "10px" }} />
        {d.name.first}
      </li>)
    }
    </ul>
  )
}


const App = ({ states, actions }) => {

  var [init, setInit] = useState(false);
  var [state, setState] = useState(states());

  var dataLoaded = () => (state.loading === false) && 
    (state.data.length > 0);

  if (!init) {
    setInit(true);
    states.map(setState);
  }

  const displayUsers = async () => {
    if (!init) { return };
    actions.setLoading(true);
    fetchUsers(actions)
  }

  return (
    <div>
      <button onClick={displayUsers}>LoadRandomUsers</button>
      <div>
        {dataLoaded() && listUsers(state.data)}
        {state.loading && <h1>searching</h1>}
      </div>
    </div>
  );
};


export default App;

Enter fullscreen mode Exit fullscreen mode

Discussion

pic
Editor guide
Collapse
alfredosalzillo profile image
Alfredo Salzillo

This is over complicated.

Collapse
artydev profile image
artydev Author

Thank you Alfredo,

In fact this example put in evidence many concepts :

  • Unidirectional Data Flow (Sam Pattern)
  • Meioisis Pattern (implementation of Sam Pattern)
  • Timed Fetch Request.
  • Streams (reactive variables)
  • Merge State (deep merging objects)

Those are simple concepts, please see why I am using them in my previous posts, to understand their uses
But I understand why you say that. I have not taken time to comments.
I don't want to spend time to do it, if there is no interests.

Of course, I can answer all your questions, consider this example as a teasing :-)

To be complete I must say that the framework I use the most is Mithril.
Look at this : MithrilUsers

Regards

Collapse
artydev profile image
artydev Author

I have cleaned up the example :-)

Collapse
jackmellis profile image
Jack

So over complicated. Let's see, dataLoaded implies a boolean value but it's a function that's actually called on every render (so why not just make it a boolean) displayUsers is an async function that never actually does anything async (it calls another async function but doesnt await it). You're storing the api response in a global variable which is just begging to cause bugs. Offloading everything into generic actions and states makes it much harder to follow. I don't see you setting loading to false when you've finished fetching, only on error. I could go on but my point is youve tried to show how data fetching could be simple, with one of the most awkward and convoluted examples ever.

Collapse
artydev profile image
artydev Author

Hello,
+1 for not awaiting...and others.

I have not published the 'index.js' part, perhaps it will make you understand my way of doing so.
And in fact, when I say simpler I refered precisely to this part...

import React from "react";
import ReactDOM from "react-dom";

import App from "./App";

import merge from "mergerino";
import { stream, scan } from "flyd";

var Actions = update => {
  return {
    loadData: results =>
      update({
        data: results,
        loading: false
      }),
    setLoading: status =>
      update({
        loading: status
      })
  };
};

var initialState = {
  start: false,
  data: [],
  loading: false
};

// reactive variable
var update = stream();
// kind of Redux :-)
var states = scan(merge, initialState, update);
// actions triggered from the view
var actions = Actions(update);
// view = f(state, actions)
ReactDOM.render(<App states={states} actions={actions} />, root);
Enter fullscreen mode Exit fullscreen mode

I let the example as it is, in order to have comments on it :-)
Don't hesitate to read my others posts and correct me

Also you can read my later post on this subject :
reactQuery

Regards