epicweb-dev / react-suspense Goto Github PK
View Code? Open in Web Editor NEWReact Suspense workshop
Home Page: https://epicreact.dev/suspense
License: Other
React Suspense workshop
Home Page: https://epicreact.dev/suspense
License: Other
You're creating the resource inside startTransition
callback:
startTransition(() => {
setPokemonResource(getPokemonResource(newPokemonName))
})
Maybe it would be better to create the resource synchronously, and only update the state inside startTransition
? That way, the requests will be sent as early as possible:
const newPokemonResource = getPokemonResource(newPokemonName)
startTransition(() => {
setPokemonResource(newPokemonResource)
})
This might be related to #37 but the following line of code will not be handled by the ErrorBoundary
https://github.com/kentcdodds/react-suspense/blob/c6a440d3b04b5ba6693423b6c8e65f5fd9c8dbaa/src/pokemon.js#L77
And I believe the following will not work as well although I was not able to trigger it.
https://github.com/kentcdodds/react-suspense/blob/c6a440d3b04b5ba6693423b6c8e65f5fd9c8dbaa/src/pokemon.js#L68-L70
The reason is ErrorBoundaries cannot catch errors thrown in Asynchronous code and since those lines live in the then block it does not handle it well.
Note
Error boundaries do not catch errors for:
- Event handlers (learn more)
- Asynchronous code (e.g. setTimeout or requestAnimationFrame callbacks)
- Server side rendering
- Errors thrown in the error boundary itself (rather than its children)
The fix that I found was to it return an object with a message property.
return Promise.reject({
message: `No pokemon with the name "${name}"`
});
It needs to be an object with the message property since ErrorBoundary expects to get that at least for this implementation.
https://github.com/kentcdodds/react-suspense/blob/c6a440d3b04b5ba6693423b6c8e65f5fd9c8dbaa/src/pokemon.js#L272-L282
It might not work if other properties from the error argument is access by the code handling the Error Boundary.
I have a codesandbox that displays the behavior and also the fix if you replace the return statement by uncommenting.
Codesandbox
The interesting thing is this error does not show up in when you run 01.js
in either final
or exercise
. I believe it is due to 02.js
putting the resource using the useState
hook.
I will be happy to add in a PR if the proposed change is the correct way to do this.
PS. Dan has a way to handle those as an abstraction and I might try it out next time.
Hi kent,
I've just encountered a bug that I can't explain and fix.
react-suspense
repo{"status":500,"message":"Cannot read property 'includes' of undefined"}
&
nav-bar.js:49 Uncaught TypeError: Cannot read property 'toLowerCase' of undefined at NavBar (nav-bar.js:49) at renderWithHooks (react-dom.development.js:15590) at updateFunctionComponent (react-dom.development.js:18021) at mountLazyComponent (react-dom.development.js:18381) at beginWork (react-dom.development.js:19961) at HTMLUnknownElement.callCallback (react-dom.development.js:310) at Object.invokeGuardedCallbackDev (react-dom.development.js:359) at invokeGuardedCallback (react-dom.development.js:421) at beginWork$1 (react-dom.development.js:25127) at performUnitOfWork (react-dom.development.js:23859) at workLoopConcurrent (react-dom.development.js:23845) at renderRootConcurrent (react-dom.development.js:23802) at performConcurrentWorkOnRoot (react-dom.development.js:23035) at workLoop (scheduler.development.js:595) at flushWork (scheduler.development.js:550) at MessagePort.performWorkUntilDeadline (scheduler.development.js:162)
I will continue to investigate & specify the issue if I find it
Code: https://github.com/kentcdodds/react-suspense/blob/main/src/final/02.js
I receive an "Uncaught ReferenceError: process is not defined" error when searching for a Pokemon that doesn't exist (ex: "errortest"). Consequently, the "try again" button is not clickable.
React Error Boundary is not working properly.
Pokemon Error Boundary catches the error but then after a few seconds falls back to webpack error screen.
falls back to webpack errors
when I close the checkbox to the side
I don't see this behavior in the videos.
My Code for exercise 06.js but I see this kind of behavior in every exercise.
// Suspense with a custom hook
// http://localhost:3000/isolated/exercise/06.js
import * as React from 'react'
import { fetchPokemon, getImageUrlForPokemon, PokemonInfoFallback, PokemonForm, PokemonDataView, PokemonErrorBoundary, } from '../pokemon' import {createResource, preloadImage} from '../utils'
const PokemonInfo = React.lazy(() => import('../lazy/pokemon-info-render-as-you-fetch'), )
const SUSPENSE_CONFIG = { timeoutMs: 4000, busyDelayMs: 300, busyMinDurationMs: 700, }
const pokemonResourceCache = {}
function getPokemonResource(name) { const lowerName = name.toLowerCase() let resource = pokemonResourceCache[lowerName] if (!resource) { resource = createPokemonResource(lowerName) pokemonResourceCache[lowerName] = resource } return resource }
function createPokemonResource(pokemonName) { const data = createResource(fetchPokemon(pokemonName)) const image = createResource(preloadImage(getImageUrlForPokemon(pokemonName))) return {data, image} }
`function usePokemonResource(pokemonName) {
const [startTransition, isPending] = React.useTransition(SUSPENSE_CONFIG)
const [pokemonResource, setPokemonResource] = React.useState(null)
React.useEffect(() => {
if (!pokemonName) {
setPokemonResource(null)
return
}
startTransition(() => {
setPokemonResource(getPokemonResource(pokemonName))
})
}, [pokemonName, startTransition])
return [pokemonResource, isPending]
}`
`function App() {
const [pokemonName, setPokemonName] = React.useState('')
const [pokemonResource, isPending] = usePokemonResource(pokemonName)
function handleSubmit(newPokemonName) {
setPokemonName(newPokemonName)
}
function handleReset() {
setPokemonName('')
}
return (
pokemon-info ${isPending ? 'pokemon-loading' : ''}
}>export default App
In the first extra credit of exercise 3 about useTransition
, the related video demonstrates that if you set delay = 200
inside createPokemonResource
, the opacity won't be shown because the pokemon-loading
class has transition-delay: 0.4s;
.
That's entirely true unless you have set a Request min time
and/or Request variable time
inside your DevTools. The default value of these variables is 400 and if you don't set them to 0, you'll end up with a delay greater than 0.4s.
Following the function from @kentcdodds/react-workshop-app/server
:
function getDefaultDelay() {
const variableTime = ls(getKey('variable_request_time'), 400);
const minTime = ls(getKey('min_request_time'), 400);
return Math.random() * variableTime + minTime;
}
If you have the default request time values of 400, you'll have at least more than 400ms of delay. If you sum the custom delay added in createPokemonResource
, you'll have 600ms or more.
I think this should be clarified in the exercise explanation because it can easily lead to confusion.
Hi Kent, I play with two implements below and both work. Is my second implement right to do? Am I missing something here?
resource
inside App
component in final/02.js
like this: const [pokemonResource, setPokemonResource] = React.useState(null)
React.useEffect(() => {
if (!pokemonName) {
setPokemonResource(null)
return
}
setPokemonResource(createPokemonResource(pokemonName))
}, [pokemonName])
// you can ignore useMemo
let pokemonResource = React.useMemo(
() => (pokemonName ? createPokemonResource(pokemonName) : null),
[pokemonName]
);
When i enter 'aaa' as a pokemon name in the form, the ui of the ErrorFallback component is shown during a fraction of second then the following error is displayed on the screen as a not catched exception:
**Error: Unsupported pokemon: "aaaa". Try "mewtwo"
(anonymous function)
D:/prof/react2/react-suspense/src/pokemon.js:68
65 | })
66 | .then(response => {
67 | if (response.errors) {
68 | return Promise.reject(
| ^ 69 | new Error(response.errors.map(e => e.message).join('\n')),
70 | )
71 | }**
I've just setup and launched the project locally and...
The titles are displayed correctly on the exercise pages:
There is no the problem in production version though.
P.S.: OS - Windows 10.
I run only this command:
npm run setup --silent
All output:
/bin/sh: yarn: command not found
⚠️ "/Users/szicar01/Repositories/concurrent-react" has a yarn.lock file, but this system does not have the right version of yarn installed. We'll install using npm instead, but you may experience issues. Install the correct version of yarn to get rid of this warning.
📦 starting `npm install --no-package-lock` in "/Users/szicar01/Repositories/concurrent-react"
SOLINK_MODULE(target) Release/.node
CXX(target) Release/obj.target/fse/fsevents.o
SOLINK_MODULE(target) Release/fse.node
SOLINK_MODULE(target) Release/.node
CXX(target) Release/obj.target/fse/fsevents.o
SOLINK_MODULE(target) Release/fse.node
husky > Setting up git hooks
husky > Done
added 1917 packages from 817 contributors and audited 907076 packages in 39.016s
found 0 vulnerabilities
🎉 finished installing dependencies in "/Users/szicar01/Repositories/concurrent-react"
💯 You're all set up! 👏
Error: Failed to load parser '@typescript-eslint/parser' declared in 'package.json » eslint-config-react-app#overrides[0]': Cannot find module 'typescript'
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:581:15)
at Function.Module._load (internal/modules/cjs/loader.js:507:25)
at Module.require (internal/modules/cjs/loader.js:637:17)
at require (/Users/szicar01/Repositories/concurrent-react/node_modules/v8-compile-cache/v8-compile-cache.js:161:20)
at Object.<anonymous> (/Users/szicar01/Repositories/concurrent-react/node_modules/@typescript-eslint/typescript-estree/dist/parser.js:17:25)
at Module._compile (/Users/szicar01/Repositories/concurrent-react/node_modules/v8-compile-cache/v8-compile-cache.js:194:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
at Module.load (internal/modules/cjs/loader.js:599:32)
at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
at Function.Module._load (internal/modules/cjs/loader.js:530:3)
Determining test suites to run...%
Anyway npm start
works!
node setup
causes the following issue:
This computer has [email protected] installed, but node@12 || 14 || 15 || 16 is required. Please update node: https://nodejs.org/.
Hi! I've been trying to execute npm run setup --silent
in order to configure the project but I'm getting the following error in the Project Validation step:
Oops! Something went wrong! :(
ESLint: 7.20.0
Error: Failed to load plugin 'import' declared in 'package.json » eslint-config-react-app': Cannot find module '/Users/__user__/epic-react/react-suspense/node_modules/eslint-plugin-import/node_modules/doctrine/lib/doctrine.js'. Please verify that the package.json has a valid "main" entry
at tryPackage (internal/modules/cjs/loader.js:303:19)
at Function.Module._findPath (internal/modules/cjs/loader.js:516:18)
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:867:27)
at Function.Module._load (internal/modules/cjs/loader.js:725:27)
at Module.require (internal/modules/cjs/loader.js:952:19)
at require (/Users/__user__/epic-react/react-suspense/node_modules/v8-compile-cache/v8-compile-cache.js:159:20)
at Object.<anonymous> (/Users/__user__/epic-react/react-suspense/node_modules/eslint-plugin-import/lib/ExportMap.js:645:130)
at Module._compile (/Users/__user__/epic-react/react-suspense/node_modules/v8-compile-cache/v8-compile-cache.js:192:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
at Module.load (internal/modules/cjs/loader.js:928:32)
🚨 Failure: Project Validation. Please review the messages above for information on how to troubleshoot and resolve this issue.
I'm not sure how to fix this or if there's a workaround, but I'd really appreciate any help.
This is the information about my NodeJS installation:
API response:
{"data":{"errors":[{"message":"Unsupported pokemon: \"pikacha\". Try \"bulbasaur\""}]}}
but the function does not check data
before getting the errors:
if (response.errors) {
return Promise.reject(
new Error(response.errors.map(e => e.message).join('\n')),
)
}
in addition it rejects a promise with a string
message instead of Error
object:
return Promise.reject(`No pokemon with the name "${name}"`)
so ErrorFallback
can't handle it and shows nothing
Hi Kent, just noticed this minor 'startTranistion' typo in the sidebar on this module:
https://epicreact.dev/modules/react-suspense/usetransition-solution
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.