React Learning Note 2023
- React state is updated in a “batch”
- Update react state with a new object, do not mute existing one
- Declarative UI
- React-redux
- Hooks
- React中的变量
- useEffect
- useMemo
- two ways to store previous props
- fetch data的两个问题
- Effect Event
- How to review effect dependencies
- Object and function compare
- useMemo and useCallback
- forwardRef
- Custom Hook
- strict mode
- Other Rules
React state is updated in a “batch”
This means that you can not get the state immediately after you change it.
number will be 1 after one click:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
To make the number to be 3, pass a update function to the setNumber function. An update function will be queued and executed later.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Update react state with a new object, do not mute existing one
Use …
, the object spread operator:
1 2 3 4 |
|
Note that spread syntax is shallow: it only copies one level deep. To update nested object:
1 2 3 4 5 6 7 |
|
For updating array object:
1 2 3 4 |
|
Immer
is a popular library that lets you write using the convenient but mutating syntax and takes care of producing the copies for you.
1 2 3 |
|
Using Immer
for array:
1 2 3 4 |
|
Declarative UI
- Declarative programming means describing the UI for each visual state rather than micromanaging the UI (imperative).
- When developing a component, Think in declarative UI way:
- Identify all its visual states.
- Determine the human and computer triggers for state changes.
- Model the state with useState.
- Remove non-essential state to avoid bugs and paradoxes.
- Connect the event handlers to set state.
React-redux
- useReducer+useContext?
- Provider
- Context
Hooks
- useContext: 跨层传输props,不用一层一层传下去
- useEffect: Use them to synchronize your component with a system outside of React.
React中的变量
- Props:immutable, 触发rerender,不记忆(retained by component)
- State:immutable, 触发rerender,记忆(retained by React)
- useRef:mutable, 不触发rerender,记忆(retained by React)
useEffect
Effects let you specify side effects that are caused by rendering itself, rather than by a particular event.
Effects run at the end of a commit after the screen updates. That is, useEffect “delays” a piece of code from running until that render is reflected on the screen.
clean up function
You can use a clean up function to clean up the effect. For example, if you subscribe to an external data source, you can unsubscribe it in the clean up function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
React will call your cleanup function each time before the next Effect runs again, and one final time when the component unmounts (gets removed). That is, the cleanup function runs not only during unmount, but before every re-render with changed dependencies.
useMemo
useMemo和useEffect都可以加依赖,但是useMemo在render过程起作用,而useEffect在commit之后起作用。 所以,如果是render依赖的变量值,用useMemo,不用useEffect+useState。
不推荐:
1 2 3 4 5 6 7 8 9 |
|
推荐:
1 2 3 4 5 6 7 8 9 |
|
two ways to store previous props
useRef
prevProps
updates after render:
1 2 3 4 |
|
useState
prevItems
is ready when render:
1 2 3 4 |
|
fetch data的两个问题
race condition
输入特别快的时候,很多search的request连续发出,不能保证回来的顺序,会出问题。 解决方法:给useEffect提供cleanup函数解决
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
undo
没看懂:https://react.dev/learn/you-might-not-need-an-effect#fetching-data
Effect Event
what is reactive
variables which can change due to a re-render * Logic inside event handlers (or Effect Event) is not reactive. * Logic inside Effects is reactive.
Problem
1 2 3 4 5 6 7 8 9 10 11 12 |
|
When reconnected, a notification will be shown, and the notificaiton will consider the theme. But when the theme changes, the notification will also be shown, which is not expected.
Solution:
Use Effect Event to separate this non-reactive logic from the reactive Effect around it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
You can think of Effect Events as being very similar to event handlers. The main difference is that event handlers run in response to a user interactions, whereas Effect Events are triggered by you from Effects. Effect Events let you “break the chain” between the reactivity of Effects and code that should not be reactive.
Supress react lint error
React linter ask you to add all reactive variables into the Effect dependencies. Effect Events let you fix many patterns where you might be tempted to suppress the dependency linter.
How to review effect dependencies
Every time you adjust the Effect’s dependencies to reflect the code, look at the dependency list. Does it make sense for the Effect to re-run when any of these dependencies change? Sometimes, the answer is “no”:
- You might want to re-execute different parts of your Effect under different conditions.
- You might want to only read the latest value of some dependency instead of “reacting” to its changes.
- A dependency may change too often unintentionally because it’s an object or a function.
ways to review and fix this
- Should this code move to an event handler?
- Is your Effect doing several unrelated things?
- If different parts of your Effect should re-run for different reasons, split it into several Effects.
- Are you reading some state to calculate the next state?
- Use update function. Use
setMessages([...messages, receivedMessage])
instead ofsetMessages(msgs => [...msgs, receivedMessage])
- Use update function. Use
- 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.
- Move static objects and functions outside your component
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
* Move dynamic objects and functions inside your Effect
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
- Read primitive values from objects
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Object and function compare
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
|
In the example above, the input only updates the message state variable. From the user’s perspective, this should not affect the chat connection. However, every time you update the message, your component re-renders. When your component re-renders, the code inside of it runs again from scratch.
A new options object is created from scratch on every re-render of the ChatRoom component. React sees that the options object is a different object from the options object created during the last render. This is why it re-synchronizes your Effect (which depends on options), and the chat re-connects as you type.
This problem only affects objects and functions. In JavaScript, each newly created object and function is considered distinct from all the others. It doesn’t matter that the contents inside of them may be the same!
Object and function dependencies can make your Effect re-synchronize more often than you need.
This is why, whenever possible, you should try to avoid objects and functions as your Effect’s dependencies. Instead, try moving them outside the component, inside the Effect, or extracting primitive values out of them.
useMemo and useCallback
- useMemo caches the result of calling your function.
- useCallback caches the function itself. React will not call your function.
forwardRef
First, get familar with useRef
:
useRef
ref.current is set during the commit process, not render process, so do not read or write ref.current during rendering. We can use ref.current in event handler or useEffect.
basic concepts
1 2 3 4 5 6 7 8 |
|
The ref attribute passed by the parent component. The ref can be an object or a function. You should either * pass the ref you receive to another component, or * pass it to useImperativeHandle.
expose dom node
The parent Form
component accesses the \ DOM node exposed by MyInput
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
expose an object
Use useImperativeHandle
to expose an object referenced by ref
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Custom Hook
You must follow these naming conventions:
- React component names must start with a capital letter, like StatusBar and SaveButton. React components also need to return something that React knows how to display, like a piece of JSX.
- Hook names must start with use followed by a capital letter, like useState (built-in) or useOnlineStatus (custom, like earlier on the page). Hooks may return arbitrary values.
This convention guarantees that you can always look at a component and know where its state, Effects, and other React features might “hide”. For example, if you see a getColor() function call inside your component, you can be sure that it can’t possibly contain React state inside because its name doesn’t start with use. However, a function call like useOnlineStatus() will most likely contain calls to other Hooks inside!
If your linter is configured for React, it will enforce this naming convention.
Note that custom Hooks only share stateful logic, not state itself.
strict mode
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.
Other Rules
data from parent to child
When child components update the state of their parent components in Effects, the data flow becomes very difficult to trace. Since both the child and the parent need the same data, let the parent component fetch that data, and pass it down to the child. This is simpler and keeps the data flow predictable: the data flows down from the parent to the child.
useSyncExternalStore
useEffect dependencies
All variables from the component body used by the Effect should be in the Effect dependency list. However, you could instead “prove” to the linter that these values aren’t reactive values, i.e. that they can’t change as a result of a re-render. For example, if serverUrl and roomId don’t depend on rendering and always have the same values, you can move them outside the component. Now they don’t need to be dependencies:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|