GithubHelp home page GithubHelp logo

epicweb-dev / react-suspense Goto Github PK

View Code? Open in Web Editor NEW
797.0 19.0 363.0 15.33 MB

React Suspense workshop

Home Page: https://epicreact.dev/suspense

License: Other

JavaScript 3.93% CSS 29.75% Dockerfile 0.22% TypeScript 65.52% Shell 0.08% MDX 0.50%
kcd-edu react suspense concurrent epicreact-dev

react-suspense's Introduction

Simplify your Async UI and improve your User Experience

Learn how Suspense works under the hood, preparing you for the future of asynchronous state management.



Build Status GPL 3.0 License Code of Conduct

Prerequisites

  • Install the React DevTools (Chrome (recommended), Firefox)
  • Experience with React and most hooks

Pre-workshop Resources

Here are some resources you can read before taking the workshop to get you up to speed on some of the tools and concepts we'll be covering:

System Requirements

  • git v2.18 or greater
  • NodeJS v18 or greater
  • npm v8 or greater

All of these must be available in your PATH. To verify things are set up properly, you can run this:

git --version
node --version
npm --version

If you have trouble with any of these, learn more about the PATH environment variable and how to fix it here for windows or mac/linux.

Setup

This is a pretty large project (it's actually many apps in one) so it can take several minutes to get everything set up the first time. Please have a strong network connection before running the setup and grab a snack.

Follow these steps to get this set up:

git clone --depth 1 https://github.com/epicweb-dev/react-suspense.git
cd react-suspense
npm run setup

If you experience errors here, please open an issue with as many details as you can offer.

The Workshop App

Learn all about the workshop app on the Epic Web Getting Started Guide.

Kent with the workshop app in the background

react-suspense's People

Contributors

allcontributors[bot] avatar andriybas avatar aprillion avatar billfienberg avatar bobbywarner avatar cawel avatar cesarcf avatar creador-dev avatar emzoumpo avatar foxandxss avatar hypnosphi avatar intrueder avatar jacobparis avatar jasikpark avatar jcarty avatar kentcdodds avatar lauchness avatar lucianoayres avatar marioleed avatar michaeldeboey avatar newyork-anthonyng avatar optimalemre avatar pritamsangani avatar pvinis avatar pvujic avatar ractoon avatar waxidiotic avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-suspense's Issues

Issue on exercice 7

Hi kent,

I've just encountered a bug that I can't explain and fix.

Reproduction

GET http://localhost:3000/pokemoney/mew 500 (Internal Server Error)
{"status":500,"message":"Cannot read property 'includes' of undefined"}

&

Uncaught TypeError: Cannot read property 'toLowerCase' 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)

Capture d’écran 2021-02-26 aΜ€ 22 25 22

I will continue to investigate & specify the issue if I find it

Error: Failed to load parser '@typescript-eslint/parser'

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!

ErrorBoundary unable to handle Errors throw in Async function

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.

facebook/react#14981 (comment)

Error using node version 16

When using nvm and node version 16 the node setup command fails.
image

The reason I ran into this issue, was because the previous module "testing" required node 16 to work.

EDIT: This is in relation to a windows dev env.

Problem in exercise 2

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 | }**

Does it make sense to create the resource eagerly?

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)
})

Unable to start the application

The npm run setup --silent fails

epic-react-suspence-fail

Other commands also fail:

  1. npm install --legacy-peer-deps
  2. yarn start

I run Node v15.8.0, git version 2.15.0, npm version 7.5.2.

Exercise 3.1: Delay doesn't work as expected with opacity transition

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.

Problem executing validation step in setup

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:

  • Node: v14.16.0
  • NPM: 7.6.0

Error Boundary not working properly

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.

image

falls back to webpack errors

image

when I close the checkbox to the side

image

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 (





<div className={pokemon-info ${isPending ? 'pokemon-loading' : ''}}>
{pokemonResource ? (

<React.Suspense
fallback={}
>

</React.Suspense>

) : (
'Submit a pokemon'
)}


)
}`

export default App

fetchPokemon does not handle errors correctly

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

Exercise 2: Derived state for pokemonResource based on pokemonName?

Hi Kent, I play with two implements below and both work. Is my second implement right to do? Am I missing something here?

1. The implementation of 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])

2. I replace with the code below, and it still works.

// you can ignore useMemo
  let pokemonResource = React.useMemo(
    () => (pokemonName ? createPokemonResource(pokemonName) : null),
    [pokemonName]
  );

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.