Last active
January 15, 2022 18:53
-
-
Save SalahHamza/cdf840c19669952e62451de5355726a9 to your computer and use it in GitHub Desktop.
Concurrent Mode Suspense example
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
/** | |
* The issues I found are the following: | |
* 1) No 'fallback', the benefit of suspense is to render as early as possible. | |
* 2) The component fetching the data should "suspend", fetching user profile in a useEffect | |
* Doesn't do that. | |
* 3) The suspense should wrap the componenet fetching the data (i.e. SuspensefulUserProfile). | |
* | |
* Extra notes: | |
* - Making a request for each profile separately is definitely something to be avoided, | |
* it would be much better to fetch the profiles together. | |
* - No error handling. The way to handle errors in this case is by adding an ErrorBounary. | |
* - Relay should be used in this case since Suspense on it's own is not ready, yet. | |
*/ | |
import { Suspense, Component } from "react"; | |
import { fetchProfileData } from "./fetchUser" | |
const SuspensefulUserProfile = ({ userId }) => { | |
const data = fetchProfileData(userId).read(); | |
return <UserProfile data={data} />; | |
}; | |
const UserProfile = ({ data }) => { | |
return ( | |
<> | |
<h1>{data.name}</h1> | |
<h2>{data.email}</h2> | |
</> | |
); | |
}; | |
const UserProfileList = () => ( | |
<> | |
<ErrorBoundary fallback={<UserProfileError />}> | |
<Suspense fallback={<UserProfileLoading />}> | |
<SuspensefulUserProfile userId={1} /> | |
</Suspense> | |
</ErrorBoundary> | |
<ErrorBoundary fallback={<UserProfileError />}> | |
<Suspense fallback={<UserProfileLoading />}> | |
<SuspensefulUserProfile userId={2} /> | |
</Suspense> | |
</ErrorBoundary> | |
<ErrorBoundary fallback={<UserProfileError />}> | |
<Suspense fallback={<UserProfileLoading />}> | |
<SuspensefulUserProfile userId={3} /> | |
</Suspense> | |
</ErrorBoundary> | |
</> | |
); | |
class ErrorBoundary extends 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; | |
} | |
} | |
const UserProfileLoading = () => { | |
return <div>Loading user profile...</div>; | |
}; | |
const UserProfileError = () => { | |
return <div>Failed to fetch user list</div>; | |
}; |
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
const users = { | |
1: { | |
name: "John", | |
email: "[email protected]" | |
}, | |
2: { | |
name: "eric", | |
email: "[email protected]" | |
}, | |
3: { | |
name: "hamza", | |
email: "[email protected]" | |
} | |
}; | |
/** | |
* Api call to fetch user profile | |
*/ | |
const fetchUserProfile = (userId) => { | |
if (users[userId]) { | |
return Promise.resolve(() => users[userId]); | |
} | |
return Promise.reject(() => new Error("User profile not found.")); | |
}; | |
/** | |
* Suspending api call to fetch user profile | |
*/ | |
const fetchUserProfileData = (userId) => { | |
return wrapPromise(fetchUserProfile(userId)); | |
}; | |
// Suspense integrations like Relay implement | |
// a contract like this to integrate with React. | |
// This code is copied from react suspense docs (not intended for prod). | |
// check here: https://codesandbox.io/s/frosty-hermann-bztrp?file=/src/index.js | |
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