react basics
hooks
- Basic Hook
- useState
- useEffect
- useContext
- Extra Hooks
- useReducer
- useCallback
- useMemo
- useRef
- useImperativeHandle
- useLayoutEffect
- useDebugValue
useState
During the initial rendering, the returned state is the same value as the first argument passed in (initialState).
The setState function is used to update state.It receives a new state value and queues a re-render of the component.
const [state, setState] = useState(initialState); // 声明
setState(newState); // 使用Note
React ensures that the identity of the setState function is stable and does not change when the component is re-rendered.This is why setState can be safely omitted from useEffect or useCallback's dependency list.
Unlike the setState method in class components, useState does not automatically merge and update objects.You can use functional setState combined with the spread operator to achieve the effect of merging and updating objects.
const [state, setState] = useState({});
setState(prevState => {
// 也可以使用 Object.assign
return {...prevState, ...updatedValues};
});useReducer is another alternative that is more suitable for managing state objects containing multiple sub-values.
// initialState 参数只会在组件的初始渲染中起作用,后续渲染时会被忽略。
// 如果初始 state 需要通过复杂计算获得,则可以传入一个函数,在函数中计算并返回初始的state,
// 此函数只在初始渲染时被调用:
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});React uses the Object.is comparison algorithm to compare state.Note that React may still need to render the component before skipping rendering.But since React doesn't unnecessarily render "deep" nodes in the component tree, there's no need to worry.If you perform expensive calculations during rendering, you can use useMemo to optimize it.
useEffect
Changing the DOM, adding subscriptions, setting timers, logging, and performing other operations with side effects are not allowed within the body of a functional component (here, in the React rendering phase), as this may create inexplicable bugs and destroy the consistency of the UI.
useEffect synchronizes with external systems after commit; it usually does not block painting, but its timing relative to paint depends on the interaction.
By default, the effect will be executed after each rendering pass, but you can choose to have it execute only when certain values change.
clear effect
Typically, resources created by an effect, such as subscriptions or timer IDs, need to be cleared when a component is uninstalled.To achieve this, the useEffect function needs to return a clearing function.Here is an example of creating a subscription:
useEffect(() => {
const subscription = props.source.subscribe();
return () => {
// 清除订阅
subscription.unsubscribe();
};
});To prevent memory leaks, the cleanup function is executed before the component is unloaded.Additionally, if a component is rendered multiple times (which it usually is), the previous effect will be cleared before the next one is executed.In the above example, this means that every update of the component creates a new subscription.
effect execution timing
Non-interaction useEffect work usually runs after paint; interaction-triggered work may run before it. After-paint execution is not guaranteed. Use useLayoutEffect for pre-paint measurement or adjustments.Effect timing.
However, not all effects can be deferred.For example, a DOM change visible to the user must be executed synchronously before the browser performs the next draw, so that the user does not perceive a visual inconsistency.(Conceptually similar to the difference between passively listening to events and actively listening for events.) React provides an additional useLayoutEffect Hook to handle this type of effect.It has the same structure as useEffect, the only difference is the calling time.
When dependencies change, React runs the previous cleanup before the new setup, and cleans up on unmount. This does not guarantee execution before any new render or the next paint.
Conditional execution of effect
By default, the effect will be executed after each round of component rendering.This way, if the effect's dependencies change, it will be recreated.
However, this may be overkill in some scenarios.For example, we don't need to create a new subscription every time the component is updated, but only need to recreate it when the source prop changes.
To do this, you can pass useEffect a second parameter, which is an array of values on which the effect depends.The updated example is as follows:
useEffect(
() => {
const subscription = props.source.subscribe();
return () => {
subscription.unsubscribe();
};
},
[props.source], // 此时,只有当 props.source 改变后才会重新创建订阅。
);Note
If you want to use this optimization, make sure that the array contains all variables that change in the outer scope and are used in the effect, otherwise your code will reference old variables from previous renders.
An empty dependency array prevents props/state-driven reruns, but remounting still runs the Effect. Development StrictMode may add a setup → cleanup → setup cycle. Cleanup should reverse setup.
An empty-dependency Effect reads values captured by its render. Declare complete dependencies or redesign synchronization when later changes matter; do not omit dependencies to force a single run.
We recommend enabling the exhaustive-deps rule in eslint-plugin-react-hooks.This rule will warn you when incorrect dependencies are added and suggest fixes.
useContext
Receives a context object (the return value of React.createContext) and returns the current value of that context.The current context value is determined by the value prop of the <MyContext.Provider> closest to the current component in the upper component.
When the latest <MyContext.Provider> above the component is updated, this Hook will trigger re-rendering and use the latest context value passed to the MyContext provider.Even if the ancestor uses React.memo or shouldComponentUpdate, it will be re-rendered when the component itself uses useContext.
Tips
If you are familiar with the context API before contacting Hook, you should understand that useContext(MyContext) is equivalent to static contextType = MyContext or <MyContext.Consumer> in the class component.
useContext(MyContext) just allows you to read the value of context and subscribe to changes in context.You still need to use <MyContext.Provider> in the upper component tree to provide context for lower components.
const themes = {
light: {
foreground: "#000000",
background: "#eeeeee"
},
dark: {
foreground: "#ffffff",
background: "#222222"
}
};
const ThemeContext = React.createContext(themes.light);
function App() {
return (
<ThemeContext.Provider value={themes.dark}>
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme.background, color: theme.foreground }}>
I am styled by theme context!
</button>
);
}useReducer
Alternative to useState.It receives a reducer of the form (state, action) => newState and returns the current state and its matching dispatch method.(If you're familiar with Redux, you already know how this works.)
In some scenarios, useReducer is more suitable than useState. For example, the state logic is complex and contains multiple sub-values, or the next state depends on the previous state, etc.Moreover, using useReducer can also perform performance optimization for components that trigger deep updates, because you can pass dispatch to child components instead of callback functions.
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}useCallback
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);Pass an inline callback function and an array of dependencies as parameters to useCallback, and it will return a memoized version of the callback function that will only be updated when a dependency changes.This is useful when you pass callback functions to child components that are optimized and use reference equality to avoid unnecessary rendering (such as shouldComponentUpdate).
useCallback(fn, deps) is equivalent to useMemo(() => fn, deps).
Note
The dependency array is not passed as a parameter to the callback function.Although conceptually it looks like this: all values referenced in the callback function should appear in the dependencies array.In the future, compilers will become smarter and it will be possible to create arrays automatically.
It is recommended to enable the exhaustive-deps rule in eslint-plugin-react-hooks.This rule will warn you when incorrect dependencies are added and suggest fixes.
useMemo
Returns a memoized value.
Pass a "create" function and an array of dependencies as arguments to useMemo, which will only recalculate the memoized value when one of the dependencies changes.This optimization helps avoid expensive calculations on every render.
Remember, the function passed into useMemo will be executed during rendering.Please do not perform operations unrelated to rendering inside this function. Operations such as side effects fall within the scope of useEffect, not useMemo.
If no dependencies array is provided, useMemo will calculate new values on each render.
You can use useMemo as a means of performance optimization, but don't regard it as a semantic guarantee.In the future, React may choose to "forget" some previous memoized values and recalculate them on the next render, such as to free up memory for off-screen components.Start by writing code that can be executed without useMemo - then add useMemo to your code to optimize performance.
Note
Arrays of dependencies are not passed as arguments to the "create" function.Although conceptually it behaves like this: all values referenced in the "create" function should appear in the dependencies array.In the future, compilers will become smarter and it will be possible to create arrays automatically.
It is recommended to enable the exhaustive-deps rule in eslint-plugin-react-hooks.This rule will warn you when incorrect dependencies are added and suggest fixes.
useRef
useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue).The returned ref object persists throughout the lifetime of the component.
A common use case is to access child components imperatively:
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}Essentially, useRef is like a "box" that can hold a mutable value in its .current property.
You should be familiar with ref as the primary way to access the DOM.If you pass a ref object into a component in the form of <div ref={myRef} />, React will set the .current property of the ref object to the corresponding DOM node no matter how the node changes.
However, useRef() is more useful than the ref attribute.It can easily hold any mutable value, similar to how you use instance fields in a class.
This is because it creates a normal Javascript object.The only difference between useRef() and creating a {current: ...} object is that useRef will return the same ref object every time it is rendered.
Keep in mind that useRef does not notify you when the contents of the ref object change.Changing the .current property will not cause the component to re-render.If you want to run some code when React binds or unbinds the ref of a DOM node, you need to use a callback ref to achieve this.
useImperativeHandle
useImperativeHandle allows you to customize the instance value exposed to the parent component when using ref.In most cases, you should avoid using imperative code like ref.useImperativeHandle should be used with forwardRef:
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);In this case, the parent component rendering <FancyInput ref={inputRef} /> can call inputRef.current.focus().
useLayoutEffect
Its function signature is the same as useEffect, but it calls the effect synchronously after all DOM changes.You can use it to read the DOM layout and trigger re-rendering synchronously.Before the browser performs drawing, the update schedule inside useLayoutEffect will be refreshed synchronously.
Use standard useEffect whenever possible to avoid blocking visual updates.
Tips
If you are migrating code from class components to function components using Hooks, you need to note that the calling phases of useLayoutEffect and componentDidMount and componentDidUpdate are the same.However, we recommend that you use useEffect initially and only try useLayoutEffect if it breaks down.
If you use server-side rendering, remember that neither useLayoutEffect nor useEffect can be executed until the Javascript code is loaded.This is why React alerts are triggered when useLayoutEffect code is introduced in server-side rendered components.To solve this problem, you need to move the code logic to useEffect (if this logic is not needed for the first rendering), or delay the display of the component until the client rendering is completed (if the HTML is displayed garbled until useLayoutEffect is executed).
To exclude components that rely on layout effects from server-rendered HTML, you can perform conditional rendering by using showChild && <Child /> and use useEffect(() => { setShowChild(true); }, []) to delay display of the component.In this way, the UI will not appear cluttered as before before client-side rendering is completed.
useDebugValue
useDebugValue can be used to display labels for custom hooks in React developer tools.
For example, the custom Hook named useFriendStatus described in the "Custom Hooks" chapter:
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
// ...
// 在开发者工具中的这个 Hook 旁边显示标签
// e.g. "FriendStatus: Online"
useDebugValue(isOnline ? 'Online' : 'Offline');
return isOnline;
}Tips
We do not recommend that you add debug values to every custom Hook.It's most valuable when it's part of a shared library.
Delayed formatting of debug values
In some cases, formatting the display of values can be an expensive operation.There is no need to do this unless you need to check the Hook.
Therefore, useDebugValue accepts a formatting function as an optional second argument.This function will only be called when the Hook is checked.It accepts a debug value as a parameter and returns a formatted display value.
For example, a custom Hook that returns a Date value can use a formatting function to avoid unnecessary calls to the toDateString function:
useDebugValue(date, date => date.toDateString());Basic use of jsx
What is different from vue's template is that variables and expressions are written in {}, and events are bound in onXxxx.
1. Conditional judgment can be:
- if else
- ternary expression
- Logical operator && ||
2. Rendering list
Use map traversal.Remember to add the key.foreach traversal has no return value.map has.
3. Events
- Bind class methods in the constructor or use arrow functions; bind in JSX creates a function on each render, rather than only on a click.
- React uses SyntheticEvent with nativeEvent for the underlying event. Most delegated events attach to root containers in React 17+, and mainly to document in React 16 and earlier; some need special handling.
4. Form
- Controlled components (similar to vue two-way data binding)
5. Use of components
props transfer data and functions.For props type checking, you need to install the 'prop-types' plug-in for type checking.
6. Status (data) improvement
Data should be managed in high-level components
7. setState
- immutable value
- Possibly updated asynchronously
- may be merged
- state must be defined in the constructor
- Do not modify state directly, use immutable values
- React 18+ with createRoot also batches timer, Promise and native-event updates; distinguish legacy root behavior.
- Class setState shallowly merges object updates and partial state returned by updater functions; useState setters replace values. Use functional updates for previous-state dependencies.
react life cycle
1. Commonly used life cycles
When mounting: constructor, render, componentDidiMount When updating: render, componentDidUpdate When unmounting: componentWillUnmount
2. Uncommonly used life cycles
getDerivedStateFromProps、shouldComponentUpdate、render、getSnapshotBeforeUpdate
react-router
function RouterComponent(){
return(
<Router>
<Switch>
<Route exact path='/'>
<Home />
</Route>
<Route path='/project/:id'>
<Home />
</Route>
<Route path='*'>
<NotFound />
</Route>
</Switch>
</Router>
)
}
// ------------------------------------------
import {Link, useParams} from 'react-router-dom';
function Project(){
// 获取url参数如‘/project/10'
const { id } = useParams();
console.log('url params id', id);
return (<div>
<Link to='/'>首页</Link>
</div>)
}