Created
September 26, 2021 02:33
-
-
Save khanzzirfan/46fe491b3bb52d024453f81ece308b61 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
/** | |
* Since solution using Suspense API we should be looking at Render-as-you-fetch pattern. | |
* Start fetching all the required data for the next screen as early as possible, and start rendering the new screen immediately. | |
* | |
* The 3-state pattern is very common for loading any resources. | |
* It is in fact so common that it has a name. | |
* Any object containing a read function that behaves like this 3-state pattern, is called a resource. | |
* | |
* Issues | |
* 1) Suspense tag missing attribute 'fallback' prop | |
* 2) Data is not being fetched early | |
* 3) Fetching data on useEffect might cause Race condition. using UseEffect is like Fetch-Then-Render pattern we should avoid. | |
* | |
* Optional but Useful: Handling Errors | |
* Since we are using Promises, we might use catch() to handle errors. | |
* Use ErrorBoundary class and then we can put it anywhere in the tree to catch errors | |
*/ | |
import React, { Suspense } from "react"; | |
// Error boundaries currently have to be classes. | |
class ErrorBoundary extends React.Component { | |
state = { hasError: false, error: null }; | |
static getDerivedStateFromError(error) { | |
return { | |
hasError: true, | |
error | |
}; | |
} | |
render() { | |
if (this.state.hasError) { | |
return this.props.fallback; | |
} | |
return this.props.children; | |
} | |
} | |
function fetchProfileData(userId) { | |
let userPromise = fetchUserProfilePromise(userId); | |
return wrapPromise(userPromise); | |
} | |
// Suspense integrations like Relay implement | |
// a contract like this to integrate with React. | |
// Real implementations can be significantly more complex. | |
// Don't copy-paste this into your project! | |
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; | |
} | |
} | |
}; | |
} | |
const fetchUserProfilePromise = (userId) => | |
fetch(`https://jsonplaceholder.typicode.com/users/${userId}`).then((res) => | |
res.json() | |
); | |
const SuspensefulUserProfile = ({ resource }) => { | |
const data = resource.read(); | |
return <UserProfile data={data} />; | |
}; | |
const UserProfile = ({ data }) => { | |
return ( | |
<> | |
<h1>{data.name}</h1> | |
<h2>{data.email}</h2> | |
</> | |
); | |
}; | |
const UserProfileList = () => ( | |
<> | |
<Suspense fallback={<h1>Loading ...</h1>}> | |
<ErrorBoundary fallback={<h2>Could not fetch user profile.</h2>}> | |
<SuspensefulUserProfile resource={fetchProfileData(1)} /> | |
<SuspensefulUserProfile resource={fetchProfileData(2)} /> | |
<SuspensefulUserProfile resource={fetchProfileData(3)} /> | |
</ErrorBoundary> | |
</Suspense> | |
</> | |
); | |
export default UserProfileList; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment