Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Advanced React #1

Open
coryhouse opened this issue Apr 18, 2018 · 0 comments
Open

Advanced React #1

coryhouse opened this issue Apr 18, 2018 · 0 comments

Comments

@coryhouse
Copy link
Owner

coryhouse commented Apr 18, 2018

Note: I deliberately leave many of the topics below out of fundamentals.

React top level api: https://reactjs.org/docs/react-api.html

Upcoming "use" hook

Primitive for handling asynchrony.
Works like “await” inside non-async client-side components.
Wrap promises (and soon other things like context) with “use”.
If a promise passed to use isn’t resolved, “use” suspends the component’s execution by throwing an exception. The component replays when the promise resolves.
A “usable” type is a wrapper for a value. Calling “use” unwraps it.
Only for client (like most hooks).
Async server components use plain async/await.
Only non-async server components can use hooks.
Unlike other hooks, can be called conditionally. Why? Because the data lives in the promise object itself. So there’s no need to store state for “use” across updates.
Can be placed in the same spots as “await” including blocks, switch statements, and loops.
Part of the “Data fetching with Suspense” story.

UI Architect / Frontend Architect / FrontendOps /
ops - Responsibilities:

State management / Finite State Machines / Statecharts
Reusable components, scripts, styles
Design system / Style Guide (collaboration)
Dev environment (transpiling/bundling/linting...) and dev tools
Build automation
Mock APIs
Automated testing (unit/integration/visual)
App composition/slicing
Error handling
Event Modeling
Accessibility
Performance
Security
Dev tools
Dependency management
More here and here

Signs it's time to consider extracting a new component

  • Duplicated JSX (extract to the same file if not reused elsewhere) - see #8 here
  • Reuse
  • JSX gets difficult to read
  • To allow thinking about page sections in isolation (each component becomes a black box, which reduces cognitive load)
  • Props list gets long
  • Document different states in Storybook, create dedicated image tests with Chromatic/Percy/etc
  • API calls and JSX in same component - Extracting API concerns means you can feed mock data via props
  • Performance (extract an expensive portion so you can optimize its rendering)
  • To keep style selectors short (whether it be BEM or CSS Modules, smaller components = smaller namespace = can use nice short names and easily navigate). If you find yourself prefixing selectors by section like this, consider extracting a component.
  • To divide work between people/teams

Why prefer fewer components?

  • Less jumping between files
  • Minimize prop drilling and propType declaration overhead
  • Shallow trees may be easier to understand than a deeply nested tree
  • Every new file adds a little bloat to the bundle due to Webpack's bundling overhead

So it's a tradeoff.

Useful middle ground here: You can declare multiple components in a single file, but export only the "parent". This avoids jumping between files and provides a clear public API. If you prefer separate files, you can create a component folder and place all its child components in that folder, then use a barrel to export only the Header.

Brain Teaser

Why doesn't the onSubmit fire here?

Patterns for scaling

Advanced Hooks

  • useReducer - Alternative to useState. Easily test in isolation. Move state logic from component to separate file. Useful to avoid having many useState declarations and multiple calls to setState in a row. Also preferable when next state depends on the previous state (though callback form of useState also helps there). Also a valid alternative to useCallback because you can pass dispatch down instead of callback funcs. Dispatch identity is stable - it doesn't change over renders.
  • useCallback - Memoize a func so that children get the same reference with each render. Useful when children are expensive to render and thus need to avoid needless re-renders due to function props changing. In other words, child components that use React.memo, shouldComponentUpdate, or PureComponent to avoid re-renders benefit from having a parent that uses useCallback to avoid needlessly sending down a new func reference on each render.
    • Tip: Struggling with useCallback dependencies causing it to be reallocated? Pass arguments to the function instead so that it has no dependencies.
  • useMemo - Memoize an expensive value to avoid re-calculation on every render.
  • useRef - Create a reference to a DOM element or hold a value that isn't rendered
  • useImperativeHandle - Combine with useRef. Use to customize the value provided by ref. Allows parent to call arbitrary functions in child. For example, focus child's component's input. demo
  • useLayoutEffect - useEffect runs after render. This runs before render. Useful when you want to calculate position before rendering.
  • useDebugValue - Apply to custom hooks you share in a library so they have a useful label in devTools.

Example Custom Hooks

JSX in Depth

Key Advanced Features

Context

Refs

  • Think of refs on React elements as functions that are called after the component has rendered
  • Use useCallback with a ref to run some logic when the ref's target changes. useCallbackRef example)
  • React docs
  • Prone to abuse. Avoid - you may not need it. Can be misused to manually change the DOM.
  • 3 common valid uses:
    • 1. Read and change DOM: focus, and calculate size and position.
    • 2. Hold values not used in render in func components (can use instance vars in classes).
    • 3. Forward refs to leaf nodes to support programmatically setting focus on elements. Useful for alerts, dialogs, textInputs. This way when something new appears/changes, you can programmatically focus the element.
  • Detailed blog post "The Complete Guide to React Refs"
    • Refs are basically a special case of useState that doesn't trigger a re-render
    • Set its value directly (instead of using a setter) via myRef.current = 'val here'
    • Get value via .current property: myRef.current
    • 4 approaches - cheatsheet:
      • String refs (deprecated)
      • Callback refs (clunkier syntax than createRef, but can be preferable when dealing with dynamic refs - otherwise prefer createRef or useRef below)
      • createRef (Classes only - simpler syntax than callbacks)
      • useRef (Funcs only) UseRef is basically this

Other Advanced Features

State

Props

Performance

Old alternatives to React.lazy:

Forms

I recommend plain React. Here's a poll. But, many options. A few popular options:

Maintainability

Debugging

Server Rendering

Deployment

  • If users leave their tab open for days/weeks, they may request an old file when they click a link. Avoid users getting errors by requesting an old file that's no longer deployed by including the site version in a cookie. Use that to say "A new version of the site is available. Click OK to reload the site." as suggested by Rich Harris.

More?

  • Show and tell! What patterns have you found useful?
@coryhouse coryhouse mentioned this issue Apr 18, 2018
13 tasks
@coryhouse coryhouse changed the title React Design Patterns Design Patterns Apr 18, 2018
@coryhouse coryhouse changed the title Design Patterns React Patterns Apr 19, 2018
@coryhouse coryhouse changed the title React Patterns Advanced React Feb 19, 2019
housemax77 pushed a commit to housemax77/reactjsconsulting that referenced this issue Jun 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant