Created
July 2, 2021 16:58
-
-
Save brandon-pereira/93acabc85cf330cbfcf1b70e7dcb6b7c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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