Varadan13

Go Back
Last updated on

React snippets library


Rendering component only in the client

import { useState, useEffect } from "react";

export const useClientOnly = () => {
  const [hasMounted, setHasMounted] = useState(false);

  useEffect(() => {
    setHasMounted(true);
  }, []);

  return hasMounted;
};

export const ClientOnly = ({ children }) => {
  const hasMounted = useClientOnly();

  if (!hasMounted) {
    return null;
  }

  return <>{children}</>;
};

Loading libraries only in the client

const Lottie =
  typeof window === "undefined"
    ? () => <></>
    : dynamic(() => import("react-lottie"));

Rendering component only in client using suspense boundary.

<Suspense fallback={<Loading />}>
  <Chat />
</Suspense>;

function Chat() {
  if (typeof window === "undefined") {
    throw Error("Chat should only render on the client.");
  }
  // ...
}

A dumb component that activates the suspense boundary.

let pokemon;
let pokemonPromise = fetchPokemon("pikachu").then(p => (pokemon = p));

function PokemonInfo() {
  if (!pokemon) {
    throw pokemonPromise;
  }
  return (
    <div>
      <div className="pokemon-info__img-wrapper">
        <img src={pokemon.image} alt={pokemon.name} />
      </div>
      <PokemonDataView pokemon={pokemon} />
    </div>
  );
}

function App() {
  return (
    <div className="pokemon-info-app">
      <div className="pokemon-info">
        <React.Suspense fallback={<div>Loading Pokemon...</div>}>
          <PokemonInfo />
        </React.Suspense>
      </div>
    </div>
  );
}

A component that activates the suspense and error boundaries.

function createResource(promise) {
  let status = "pending";
  let result = promise.then(
    resolved => {
      status = "success";
      result = resolved;
    },
    rejected => {
      status = "error";
      result = rejected;
    }
  );
  return {
    read() {
      if (status === "pending") throw result;
      if (status === "error") throw result;
      if (status === "success") return result;
      throw new Error("This should be impossible");
    },
  };
}

function createPokemonResource(pokemonName) {
  return createResource(fetchPokemon(pokemonName));
}

function App() {
  const [pokemonName, setPokemonName] = React.useState("");
  const [pokemonResource, setPokemonResource] = React.useState(null);

  React.useEffect(() => {
    if (!pokemonName) {
      setPokemonResource(null);
      return;
    }
    setPokemonResource(createPokemonResource(pokemonName));
  }, [pokemonName]);

  function handleSubmit(newPokemonName) {
    setPokemonName(newPokemonName);
  }

  function handleReset() {
    setPokemonName("");
  }

  return (
    <div className="pokemon-info-app">
      <PokemonForm pokemonName={pokemonName} onSubmit={handleSubmit} />
      <hr />
      <div className="pokemon-info">
        {pokemonResource ? (
          <PokemonErrorBoundary
            onReset={handleReset}
            resetKeys={[pokemonResource]}
          >
            <React.Suspense
              fallback={<PokemonInfoFallback name={pokemonName} />}
            >
              <PokemonInfo pokemonResource={pokemonResource} />
            </React.Suspense>
          </PokemonErrorBoundary>
        ) : (
          "Submit a pokemon"
        )}
      </div>
    </div>
  );
}

A promise wrapper to use inside a component that must trigger suspense boundary

a resource management system for React Suspense

function createResource(promise) {
  let status = "pending";
  let result = promise.then(
    resolved => {
      status = "success";
      result = resolved;
    },
    rejected => {
      status = "error";
      result = rejected;
    }
  );
  return {
    read() {
      if (status === "pending") throw result;
      if (status === "error") throw result;
      if (status === "success") return result;
      throw new Error("This should be impossible");
    },
  };
}

A component that triggers suspend boundaries using the use hook.

import {use} from 'react';
import { fetchData } from './data.js';

export default function Albums({ artistId }) {
  const albums = use(fetchData(`/${artistId}/albums`));
  return (
    <ul>
      {albums.map(album => (
        <li key={album.id}>
          {album.title} ({album.year})
        </li>
      ))}
    </ul>
  );
}



import { Suspense } from 'react';
import Albums from './Albums.js';

export default function ArtistPage({ artist }) {
  return (
    <>
      <h1>{artist.name}</h1>
      <Suspense fallback={<Loading />}>
        <Albums artistId={artist.id} />
      </Suspense>
    </>
  );
}

function Loading() {
  return <h2>🌀 Loading...</h2>;
}

A graphql mutation function that allows the ui to show proper error validations.

const createOnboardingData = async ({ input }) => {
  try {
    const res = await client.clientPrivate.mutate({
      client: client.clientPrivate,
      mutation: createOnboardingDataMutation,
      variables: {
        input,
      },
    });

    if (
      res &&
      Array.isArray(res?.data?.create_onboarding_data?.error) &&
      res.data.create_onboarding_data.error[0]
    ) {
      return {
        error: res.data.create_onboarding_data.error[0]?.description,
        isError: true,
      };
    }
    return {
      error: null,
      isError: false,
    };
  } catch (error) {
    return {
      error:
        "Oops! Something didn't work as expected. Please contact support if the issue persists.",
      isError: true,
    };
  }
};