DEV Community

artydev
artydev

Posted on

Fetching Data with UmaiJS

/** @jsx m */

import { m, mount, redraw } from "umai";

// Utility function to debounce inputs
function debounce(fn, delay) {
  let timer;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

function sleep(ms) {
  return new Promise((res) => {
    setTimeout(res, ms);
  });
}

async function getRandomUsers() {
  // Simulate network delay
  await sleep(State.delay);

  // Fetch random users from the Random User API
  const response = await fetch("https://randomuser.me/api/?results=5");

  if (!response.ok) {
    throw new Error("Network response was not ok");
  }

  const data = await response.json();

  return data.results.map((user, index) => ({
    id: index + 1,
    name: `${user.name.first} ${user.name.last}`,
    email: user.email,
    picture: user.picture.medium,
  }));
}

let State = {
  users: [],
  delay: 200,
  loading: false,
  error: null,
};

function saveUsers(users) {
  State.users = users;
}

function clearUsers() {
  State.users = [];
  redraw();
}

async function fetchUsers(node) {
  State.loading = true;
  State.error = null;
  redraw();

  clearUsers();
  try {
    let users = await getRandomUsers();
    saveUsers(users);
  } catch (error) {
    console.error("Failed to fetch users:", error);
    State.error = "Failed to fetch users. Please try again.";
  } finally {
    State.loading = false;
    redraw();
  }
}

// User item component for clarity and reuse
const UserItem = ({ user }) => (
  <li class="user-item">
    <img src={user.picture} alt={user.name} class="user-avatar" />
    <div class="user-details">
      <p><strong>{user.name}</strong></p>
      <p>{user.email}</p>
    </div>
  </li>
);

const Users = () => {
  return (
    <div>
      {State.loading && <Loader />}
      {State.error && <p class="error-message">{State.error}</p>}
      {!State.loading && State.users.length === 0 && (
        <p class="no-users-message">No users available. Click "Load Users" to fetch.</p>
      )}
      <ul aria-live="polite" class="user-list">
        {State.users.map((user) => (
          <UserItem user={user} key={user.id} />
        ))}
      </ul>
    </div>
  );
};

const Timer = () => {
  const handleInput = debounce((e) => {
    State.delay = parseInt(e.target.value, 10) || 0;
    redraw();
  }, 300);

  return (
    <div class="timer">
      <label for="delay-input">Delay fetch for: </label>
      <input
        type="number"
        id="delay-input"
        value={State.delay}
        oninput={handleInput}
        aria-label="Fetch delay in milliseconds"
      />
      <span> ms</span>
    </div>
  );
};

const Loader = () => {
  return (
    <div class="loader">
      Loading...
    </div>
  );
};

function setup(node) {
  let btnLoader = node.querySelector("button");
  btnLoader.addEventListener("click", () => fetchUsers(node));
}

const App = () => (
  <div dom={setup}>
    <Timer />
    <button class="load-button">Load Users</button>
    <Users />
  </div>
);

export { App };


mount(document.body, App)
Enter fullscreen mode Exit fullscreen mode
body {
  font-family: Arial, sans-serif;
  margin: 20px;
}

.timer {
  margin-bottom: 20px;
}

label {
  margin-right: 10px;
}

input {
  width: 80px;
  padding: 5px;
  font-size: 14px;
}

.load-button {
  padding: 10px 20px;
  background-color: #007bff;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}

.load-button:hover {
  background-color: #0056b3;
}

.loader {
  text-align: center;
  font-weight: bold;
  margin-bottom: 20px;
}

.error-message {
  color: red;
  text-align: center;
  margin-bottom: 10px;
}

.no-users-message {
  text-align: center;
  margin-bottom: 20px;
}

.user-list {
  list-style: none;
  padding: 0;
}

.user-item {
  display: flex;
  align-items: center;
  margin-bottom: 15px;
}

.user-avatar {
  border-radius: 50%;
  margin-right: 15px;
}

.user-details p {
  margin: 0;
}

Enter fullscreen mode Exit fullscreen mode

You can test it here :

Demo

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more