Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

⚛️ Hyper Fetch React

React hooks for HyperFetch. Automatic fetching, caching, and real-time updates with full type safety.

Image Image Image Image Image Image Image

📖 About

@hyper-fetch/react connects HyperFetch's typed request system to React's component lifecycle. useFetch triggers requests on mount and re-fetches when dependencies change. useSubmit gives you a submit function for mutations triggered by user actions. Both hooks return fully typed data, error, and loading states with built-in callbacks.

🎯 Key Capabilities

  • Data on mount, zero boilerplateuseFetch fetches, caches, and types your data automatically
  • 🎯 Mutations that just workuseSubmit handles loading, errors, and success states for form submissions
  • 🔄 Always fresh data — Auto-refetch on window focus, polling, and stale-time revalidation built in
  • Optimistic updates with rollback — Update the UI instantly and auto-revert if the request fails
  • 🔮 Full type safety — Every hook, callback, response, and error is typed end-to-end with zero any
  • 🚀 Instant page loads — Prefetch data before components mount so users never wait
  • 🔗 No more useEffect spaghetti — Dependent requests via disabled option, no manual orchestration
  • 📡 SSR out of the box — Works with Next.js, Remix, and any server-rendered framework
  • 💎 Skip the state library — No Redux or Zustand needed for server state — it's all handled

🚀 Quick Start

npm install @hyper-fetch/core @hyper-fetch/react
import { useFetch } from "@hyper-fetch/react";
import { getUsers } from "./api";

const UserList = () => {
  // Fetches on mount, returns typed data — no useEffect needed
  const { data, loading, error } = useFetch(getUsers);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Something went wrong</p>;
  return (
    <ul>
      {data?.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
};

📚 Documentation

💡 Examples

Fetch with callbacks

import { useFetch } from "@hyper-fetch/react";

const UserProfile = ({ userId }: { userId: number }) => {
  // setParams makes the request dynamic — re-fetches when userId changes
  const { data, loading, error, onSuccess, onError } = useFetch(getUser.setParams({ userId }));

  // Typed callbacks — response shape matches your request definition
  onSuccess(({ response }) => {
    console.log("Loaded user:", response.data.name);
  });

  onError(({ response }) => {
    console.error("Failed to load user:", response.error);
  });

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;
  return <h1>{data.name}</h1>;
};

Mutations with useSubmit

import { useSubmit } from "@hyper-fetch/react";

const CreateUserForm = () => {
  // useSubmit doesn't fire on mount — it waits for you to call submit()
  const { submit, submitting, onSubmitSuccess, onSubmitError } = useSubmit(createUser);

  // Callbacks fire after each submission with typed response data
  onSubmitSuccess(({ response }) => {
    alert(`User ${response.data.name} created!`);
  });

  onSubmitError(({ response }) => {
    alert(`Error: ${response.error.message}`);
  });

  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const form = new FormData(e.currentTarget);
    // Pass typed payload — TypeScript ensures the shape matches your request definition
    submit({
      data: { name: form.get("name") as string, email: form.get("email") as string },
    });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="name" placeholder="Name" required />
      <input name="email" placeholder="Email" required />
      <button type="submit" disabled={submitting}>
        {submitting ? "Creating..." : "Create User"}
      </button>
    </form>
  );
};

Dependent requests

const UserPosts = ({ userId }: { userId: number }) => {
  // First request fetches the user
  const { data: user } = useFetch(getUser.setParams({ userId }));
  // Second request waits until user is loaded — no useEffect chains needed
  const { data: posts } = useFetch(getUserPosts.setParams({ userId }), {
    disabled: !user,
  });

  return (
    <div>
      <h1>{user?.name}</h1>
      {posts?.map((post) => (
        <article key={post.id}>{post.title}</article>
      ))}
    </div>
  );
};

Optimistic updates with automatic rollback

import { useSubmit } from "@hyper-fetch/react";

const AddComment = ({ postId }: { postId: number }) => {
  // setOptimistic runs BEFORE the request — update the list instantly
  const optimisticAdd = addComment.setParams({ postId }).setOptimistic((request, client) => {
    const cacheKey = getComments.setParams({ postId });
    const previous = client.cache.get(cacheKey);

    // Immediately insert the new comment into the cached list
    const optimisticComment = { id: `temp-${Date.now()}`, text: request.data.text, author: "You" };
    client.cache.set(cacheKey, {
      ...previous,
      data: [...(previous?.data || []), optimisticComment],
    });

    return {
      context: { previous },
      // If the request fails, put the old list back
      rollback: () => client.cache.set(cacheKey, previous),
      // On success, refetch to get the real server data
      invalidate: [cacheKey],
    };
  });

  const { submit, submitting, onSubmitError } = useSubmit(optimisticAdd);

  // mutationContext is fully typed from your setOptimistic return
  onSubmitError(({ mutationContext }) => {
    console.log("Rolled back — restored", mutationContext?.previous?.data?.length, "comments");
  });

  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const text = new FormData(e.currentTarget).get("text") as string;
    submit({ data: { text } });
    e.currentTarget.reset();
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="text" placeholder="Write a comment..." required />
      <button type="submit" disabled={submitting}>
        Post
      </button>
    </form>
  );
};

License

MIT