Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save brandon-pereira/93acabc85cf330cbfcf1b70e7dcb6b7c to your computer and use it in GitHub Desktop.
Save brandon-pereira/93acabc85cf330cbfcf1b70e7dcb6b7c to your computer and use it in GitHub Desktop.
/**
* I'm going to be honest, I thought I was a 5 in React and I knew of Suspense,
* but now that I'm seeing that Concurrent React is already available this is the first
* I'm reading of this.
*
* I should have known to never give a 5 on anything in an interview :D
*
* The core issues:
* - Fallback state was not set
* - Didn't use the Suspence data fetching API (wrapPromise function below), not sure the 'proper' naming of this TBH.
* - Didn't leverage the Suspence data fetching API in the component (calling .read())
*
* Hopefully this is a good answer, I haven't tested this code in a full repo
* I more so just stepped through the documentation and integrated it here.
* In a real project/application, I would for sure have tested this inside the application
* and considered additional cases like server side rendering, better loading/error states,
* error boundaries, etc.
*/
import React, { Suspense, useRef, useState, useEffect } from 'react';
// Made a big assumption this exists!
import Loader from './Loader';
const SuspensefulUserProfile = ({ userId }) => {
const [resourceObj, setResourceObj] = useState(
_fetchUserProfileDataAsSuspense(userId)
);
const currentUserId = useRef(userId);
// This useEffect is a bit more complex than I like just because
// I didn't want it to trigger re-render on INITIAL load. Only sequential changes.
// From this example, keys dont change but I didn't want to assume that.. If I could
// ask questions, this would be one.. do the userIds change across renders.
useEffect(() => {
if (currentUserId.current !== userId) {
setResourceObj(_fetchUserProfileDataAsSuspense(userId));
currentUserId.current = userId;
}
}, [userId]);
return (
<Suspense fallback={<Loader />}>
<UserProfile data={resourceObj} />
</Suspense>
);
};
const UserProfile = ({ data }) => {
// I didn't want to rename props, I'd call this resource.
// Or even doing this a component higher if it was allowed.
const user = data.read();
return (
<>
<h1>{user.name}</h1>
<h2>{user.email}</h2>
</>
);
};
const UserProfileList = () => (
<>
<SuspensefulUserProfile userId={1} />
<SuspensefulUserProfile userId={2} />
<SuspensefulUserProfile userId={3} />
</>
);
// This variable name isn't great. I would check in an existing app
// if theres a standardized naming for these types of functions.
function _fetchUserProfileDataAsSuspense(userId) {
const promise = fetchUserProfile(userId);
return wrapPromise(promise);
}
// I stole this code from this Sandbox: https://codesandbox.io/s/frosty-hermann-bztrp?file=/src/fakeApi.js:196-857
// In a real world scenario I would look on NPM for an abstraction on this,
// especially since the documentation is very clear that this isn't the best code.
function wrapPromise(promise) {
let status = 'pending';
let result;
let suspender = promise.then(
(r) => {
status = 'success';
result = r;
},
(e) => {
status = 'error';
result = e;
}
);
return {
read() {
if (status === 'pending') {
throw suspender;
} else if (status === 'error') {
throw result;
} else if (status === 'success') {
return result;
}
}
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment