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,
};
}
};