React Docs 2023 ADVANCED

917 阅读7分钟

中文版:React Docs 2023 进阶版

React Docs 官方文档

React Docs 2023 ADVANCED

Learn

Thinking in React

  1. Break the UI into a component hierarchy
  2. Build a static version in React
  3. Find the minimal but complete representation of UI state
  4. Identify where your state should live
  5. Add inverse data flow

Describing the UI

Don’t put numbers on the left side of &&

  • bad: messageCount && <p>New messages</p>
  • good: messageCount > 0 && <p>New messages</p>
  • To test the condition, JavaScript converts the left side to a boolean automatically. However, if the left side is 0, then the whole expression gets that value (0), and React will happily render 0 rather than nothing

Detecting impure calculations with StrictMode

  • By calling the component functions twice, Strict Mode helps find components that break these rules
  • You can use Strict Mode to find mistakes in your components
  • Component, initializer, and updater functions need to be pure

Adding Interactivity

State + Event handler: add state to any component, and update it as needed

  • Hooks rely on a stable call order on every render of the same component. Internally, React holds an array of state pairs for every component React Hooks: Not Magic, Just Arrays

When you call useState, React gives you a snapshot of the state for that render. Unlike a photograph or a movie frame, the UI “snapshot” you return is interactive

  • Variables and event handlers don’t “survive” re-renders. Every render has its own event handlers. Every render (and functions inside it) will always “see” the snapshot of the state that React gave to that render. Event handlers created in the past have the state values from the render in which they were created

Managing State

What triggers state changes:

  • Human inputs, like clicking a button, typing in a field, navigating a link. (event handlers!)
  • Computer inputs, like a network response arriving, a timeout completing, an image loading.(effect sync external)

Structuring state well is very important

  1. Group related state. If you always update two or more state variables at the same time, consider merging them into a single state variable.
  2. Avoid contradictions in state. When the state is structured in a way that several pieces of state may contradict and “disagree” with each other, you leave room for mistakes. Try to avoid this.
  3. Avoid redundant state. If you can calculate some information from the component’s props or its existing state variables during rendering, you should not put that information into that component’s state.
  4. Avoid duplication in state. When the same data is duplicated between multiple state variables, or within nested objects, it is difficult to keep them in sync. Reduce duplication when you can.
  5. Avoid deeply nested state. Deeply hierarchical state is not very convenient to update. When possible, prefer to structure state in a flat way.
  6. Don’t put props into state unless you specifically want to prevent updates.
  7. For UI patterns like selection, keep ID or index in state instead of the object itself.

Preserving and Resetting State

  1. React preserves a component’s state for as long as it’s being rendered at its position in the UI tree. If it gets removed, or a different component gets rendered at the same position, React discards its state.
  2. Same component at the same position preserves state
  3. Different components at the same position reset state
  4. Resetting state at the same position: a. Render components in different positions b. Give each component an explicit identity with key.You can force a subtree to reset its state by giving it a different key.

useState and useReducer

  • useState declares a state variable that you can update directly.
  • useReducer declares a state variable with the update logic inside a reducer function.

Scaling Up with Reducer and Context

  • You can combine reducer with context to let any component read and update state above it.

Escape Hatches

Refs

  • Refs are an escape hatch to hold onto values that aren’t used for rendering. You won’t need them often
  • Like state, refs let you retain information between re-renders of a component.
  • Unlike state, setting the ref’s current value does not trigger a re-render.
  • Usually, you will use refs for non-destructive actions like focusing, scrolling, or measuring DOM elements.

Effects

  • Unlike events, Effects are caused by rendering itself rather than a particular interaction
  • Effects let you synchronize a component with some external system (third-party API, network, etc).
  • You can’t “choose” your dependencies. They are determined by the code inside the Effect.
How to remove unnecessary Effects
  • If you can calculate something during render, you don’t need an Effect.
  • To cache expensive calculations, add useMemo instead of useEffect.
  • To reset the state of an entire component tree, pass a different key to it.
  • To reset a particular bit of state in response to a prop change, set it during rendering.
  • Code that needs to run because a component was displayed should be in Effects, the rest should be in events.
  • Whenever you try to synchronize state variables in different components, consider lifting state up.
  • You can fetch data with Effects, but you need to implement cleanup to avoid race conditions (ignore).
Lifecycle of Reactive Effects
  • Components can mount, update, and unmount.
  • Each Effect has a separate lifecycle from the surrounding component.
  • Each Effect describes a separate synchronization process that can start and stop.
  • When you write and read Effects, you should think from each individual Effect’s perspective (how to start and stop synchronization) rather than from the component’s perspective (how it mounts, updates, or unmounts).
  • Values declared inside the component body are “reactive”.
  • Reactive values should re-synchronize the Effect because they can change over time.
  • The linter verifies that all reactive values used inside the Effect are specified as dependencies.
  • All errors flagged by the linter are legitimate. There’s always a way to fix the code that doesn’t break the rules.

Separating Events from Effects

  • Event handlers run in response to specific interactions.
  • Effects run whenever synchronization is needed.
  • Logic inside event handlers is not reactive.
  • Logic inside Effects is reactive.
  • You can move non-reactive logic from Effects into Effect Events.
  • Only call Effect Events from inside Effects.
  • Don’t pass Effect Events to other components or Hooks.
Removing Effect Dependencies
  • If the code in your Effect should run in response to a specific interaction, move that code to an event handler.
  • If different parts of your Effect should re-run for different reasons, split it into several Effects.
  • If you want to update some state based on the previous state, pass an updater function.
  • If you want to read the latest value without “reacting” it, extract an Effect Event from your Effect.
  • In JavaScript, objects and functions are considered different if they were created at different times.
  • Try to avoid object and function dependencies. Move them outside the component or inside the Effect.
Reusing Logic with Custom Hooks
  • Custom Hooks only share stateful logic, not state itself.
  • You can pass reactive values from one Hook to another, and they stay up-to-date.
  • All Hooks re-run every time your component re-renders.
  • The code of your custom Hooks should be pure, like your component’s code.
  • Wrap event handlers received by custom Hooks into Effect Events.
  • Don’t create custom Hooks like useMount. Keep their purpose specific.
  • It’s up to you how and where to choose the boundaries of your code.

Reference

Components

Built-in components

  • <Fragment>, alternatively written as <>...</>, lets you group multiple JSX nodes together.
  • <Profiler> lets you measure rendering performance of a React tree programmatically.
  • <Suspense> lets you display a fallback while the child components are loading.
  • <StrictMode> enables extra development-only checks that help you find bugs early.
<StrictMode>
  • lets you find common bugs in your components early during development.

  • Strict Mode enables the following development-only behaviors:

    • Your components will re-render an extra time to find bugs caused by impure rendering.
    • Your components will re-run Effects an extra time to find bugs caused by missing Effect cleanup.
    • Your components will be checked for usage of deprecated APIs.
  • Strict Mode calls some of your functions (only the ones that should be pure) twice in development. This includes:

    • Your component function body (only top-level logic, so this doesn’t include code inside event handlers)
    • Functions that you pass to useState, set functions, useMemo, or useReducer
    • Some class component methods like constructor, render, shouldComponentUpdate (see the whole list)

Hooks

  • Hooks are only valid at the top level of a component or another Hook

State Hooks

  • State lets a component “remember” information like user input
  1. useState declares a state variable that you can update directly.
  2. useReducer declares a state variable with the update logic inside a reducer function.
useState
  • set function & updater function & initializer function
  • Resetting state with a key
  • Too many re-renders: Typically, this means that you’re unconditionally setting state during render, so your component enters a loop
useReducer

Context Hooks

  • Context lets a component receive information from distant parents without passing it as props.
  1. useContext reads and subscribes to a context.

Ref Hooks

  • Refs let a component hold some information that isn’t used for rendering
  1. useRef declares a ref. You can hold any value in it, but most often it’s used to hold a DOM node.
  2. useImperativeHandle lets you customize the ref exposed by your component. This is rarely used.

Effect Hooks

  • Effects let a component connect to and synchronize with external systems.

Performance Hooks

  1. useMemo lets you cache the result of an expensive calculation.
  2. useCallback lets you cache a function definition before passing it down to an optimized component.
  3. useTransition lets you mark a state transition as non-blocking and allow other updates to interrupt it.
  4. useDeferredValue lets you defer updating a non-critical part of the UI and let other parts update first.
useCallback
  • You pass it as a prop to a component wrapped in memo. You want to skip re-rendering if the value hasn’t changed. Memoization lets your component re-render only when dependencies aren’t the same.
  • The function you’re passing is later used as a dependency of some Hook. For example, another function wrapped in useCallback depends on it, or you depend on this function from useEffect.
useMemo
  • The calculation you’re putting in useMemo is noticeably slow, and its dependencies rarely change.
  • You pass it as a prop to a component wrapped in memo. You want to skip re-rendering if the value hasn’t changed. Memoization lets your component re-render only when dependencies aren’t the same.
  • The value you’re passing is later used as a dependency of some Hook. For example, maybe another useMemo calculation value depends on it. Or maybe you are depending on this value from useEffect.
useDeferredValue
  • Showing stale content while fresh content is loading
  • Indicating that the content is stale
  • Deferring re-rendering for a part of the UI
  • How is deferring a value different from debouncing and throttling?
    • Unlike debouncing or throttling, it doesn’t require choosing any fixed delay
    • Unlike with debouncing or throttling,deferred re-renders done by useDeferredValue are interruptible by default.
    • If the work you’re optimizing doesn’t happen during rendering, debouncing and throttling are still useful.

Other Hooks

  1. useDebugValue lets you customize the label React DevTools displays for your custom Hook.
  2. useId lets a component associate a unique ID with itself. Typically used with accessibility APIs.
  3. useSyncExternalStore lets a component subscribe to an external store.
useDebugValue
  • Adding a label to a custom Hook
  • Deferring formatting of a debug value
  • We don’t recommend adding debug values to every custom Hook. It’s most valuable for custom Hooks that are part of shared libraries and that have a complex internal data structure that’s difficult to inspect.