You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
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.
Declare global event handlers in the capture phase by appending Capture to event name. onClickCapture, onHoverCapture, etc. Example
Useful for large apps, multiple apps in the org, or cross-cutting concerns
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.
useIsMounted - Check if mounted. Useful to determine whether a component has mounted for use in other custom hooks. Also useful in async processes within components. Pretty handy in try/catch/finally blocks for example.
useApiCall - Wrap API calls in a hook to encapsulate the redundancy of tracking response, loading, and error state. Enhancement: mimic the "Only one loader" behavior by incrementing a counter each time a call starts and decrementing a counter each time a call completes or errors. Share numApiCallsInProgress or an array of apiCallsInProgress with an identifier for each between hooks via context.
useLocalStorage - Every call to setState persists to localStorage
useAsync - for handling an async call with loading states and errors. blog post
useForm - simple hook for handling form state and errors
Wrap consumer in hook for convenient consumption. useAlert example. Just import useX and you have access. No need to import context and pass it to a useContext call. And can enforce that it's being called under a provider (as shown in link above)
Redux remains useful. Redux offers scalable unidirectional flows, decomposed state handling via dedicated reducers and actions, hot reloading, time travel, and potential performance benefits via selectors and connect's reference comparison. Though can consider react-waterfall to get most these benefits (albeit without Redux's huge ecosystem and excellent docs).
Static contextType (classes only, and can only consume one context this way unless you wrap the others. Bottom line, prefer useHook)
Connected components re-render when any values passed to the provider's value prop change. So don't declare the value in render. Instead, lift it to state.
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.
Use functional setState to get prevState - Codepen - Why? It's safe because when React encounters multiple functional setState() calls, instead of merging objects together, React queues the functions in the order they were called.
State reducer - Allow parent to augment/override the behavior of a child component by passing down a few props, or a reference to a single reducer. Simple example
Suggestion: Treat state as immutable: React lets you use whatever style of data management you want, including mutation. However, if you can use immutable data in performance-critical parts of your application it's easy to implement shouldComponentUpdate()/PureComponent to speed up your app.
Support passing either a string or a React component. Useful for wrapping a string in an H1 by default, but allowing someone to pass an optional component in instead for more power. Use React.isValidElement to determine if a React element has been passed in on props. Note that you output the component passed on props a var, NOT as JSX. {React.isValidElement(heading) ? heading : <H1>{heading}</H1>}
Declare in parent and pass down as a child. Accepting children increases flexibility and reduces prop drilling. This pattern can also avoid needless re-renders (see "Pass component down as prop to avoid re-render" under performance below).
Spread props. Pass through props via destructuring: const { roleId, locks, ...passThroughProps} = props;
Can use React.memo with context too, but need to "lift" context consumer to parent. Remember, with context, all children of provider rerender when the provider component renders.
Could I create one instance per page instead of one per row (e.g. Avoid tooltip per row on large table, or dialog per row on large table. Instead, create one and populate dynamically based on a row click/hover)
Remove one item at a time from a row/feature until it renders fast to help find the culprit.
Change related state at the same time to avoid redundant renders
Pass primitives (strings, numbers...) to child components to assist with diffing. Avoid passing arrow funcs and objects on props when performance is a concern since it leads to needless re-renders of child components.
Trading static for dynamic, so reduced safety and autocomplete support.
Adds decision fatigue.
Adds latency.
Adds complexity.
Easy to get wrong by accidentally statically importing the component elsewhere. Consider writing tests that assert lazy loaded components aren't part of bundle. But that's extra work too.
Consider shouldComponentUpdate/PureComponent. Why? When you call setState, React will compare the old React elements to a new set of React elements (this is called reconciliation) and then use that information to update the real DOM elements. Sometimes that can get slow if you’ve got a lot of elements to check (like a big table, list, or SVG). But only use after measuring result. Here's how to measure. Don’t imagine “I bet that code is slow”. Write your code naturally. If there are performance problems, fix them. Do not PureComponent all the things. "I added PureRenderMixin to every component. My app got slower 🤔." - Ryan Florence Why? A component does one diff. A pureComponent does one or two diffs (props and state in shouldComponentUpdate, and then the normal element diff). So if a component usually changes when there’s an update, then a PureComponent will be doing two diffs instead of just one. This means it’s going to be slower usually but faster occasionally. So if most of your components change most of the time, your app will get slower by using PureComponent everywhere because you're doing two diffs instead of one.
Call setState with null to avoid re-render when the relevant state hasn't actually changed. (useful when someone clicked a button that fires a setState call, but no state is actually changed.
Organize component folders to mimic routing path. Place reusable components in /UI. Don't repeat the path name in the component's name, but when importing, alias the default to reflect the import path. Example: import UserList from './User/List.js';
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?
The text was updated successfully, but these errors were encountered:
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
Why prefer fewer components?
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
Capture
to event name.onClickCapture
,onHoverCapture
, etc. ExampleUseful for large apps, multiple apps in the org, or cross-cutting concerns
Advanced Hooks
React.memo
,shouldComponentUpdate
, orPureComponent
to avoid re-renders benefit from having a parent that uses useCallback to avoid needlessly sending down a new func reference on each render.Example Custom Hooks
numApiCallsInProgress
or an array ofapiCallsInProgress
with an identifier for each between hooks via context.JSX in Depth
Layout
component - This can hold your header, footer, context provider(s), scenario selector.Key Advanced Features
Context
Refs
useCallback
with a ref to run some logic when the ref's target changes. useCallbackRef example)myRef.current = 'val here'
.current
property:myRef.current
Other Advanced Features
State
useState
by passing it a func. Then the initial value isn't calculated only on initial render. (Plain default values are executed on page load when the component is parsed).Props
{React.isValidElement(heading) ? heading : <H1>{heading}</H1>}
const { roleId, locks, ...passThroughProps} = props;
Performance
Old alternatives to React.lazy:
this
keywordevent.target.elements.fieldName.value
- See SearchInput here. Or extract child components to avoid bindingForms
I recommend plain React. Here's a poll. But, many options. A few popular options:
Maintainability
import UserList from './User/List.js';
Debugging
Server Rendering
Deployment
More?
The text was updated successfully, but these errors were encountered: