文章目录
- 常用的Hooks
- useState
- useReducer
- useRef
- useContext
- useMemo
- useCallback
- useEffect
- 组件通信
- Props(属性)
- Ref(引用)
- Context(上下文)
- State(状态)
- 回调函数
- Event Bus(事件总线)
- Custom Hooks(自定义钩子)
- Redux
- 生命周期
- React 17 之前:
- React 17 之后:
- 常用的生命周期方法:
- Class 组件中常用:
- Function 组件中使用(Hooks):
- 不常用的生命周期方法:
- Class 组件中不常用:
- Class 组件中特有的:
好多年没使react了,有些忘了,简单复习一下…
常用的Hooks
useState
用于管理组件内部的状态,允许在函数组件中添加和管理 state。
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
useReducer
管理复杂的状态逻辑,特别是当 state 逻辑涉及到多个子值,或者下一个 state 依赖于前一个 state 时。
import React, { useReducer } from 'react';
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:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
</>
);
}
useRef
在组件中直接访问DOM 元素;
import React, { useRef } from 'react';
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` 指向挂载的文本输入元素
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
在组件的不同渲染之间保持一个可变的值,并且这个值不会触发组件的重新渲染。
import React, { useRef, useState, useEffect } from 'react';
function Timer() {
const intervalRef = useRef();
const [count, setCount] = useState(0);
useEffect(() => {
intervalRef.current = setInterval(() => {
setCount((c) => c + 1);
}, 1000);
return () => clearInterval(intervalRef.current);
}, []);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => clearInterval(intervalRef.current)}>Stop Timer</button>
</div>
);
}
useContext
在组件之间共享状态,而不必通过 “props下钻” 层层传递。
import React, { useContext } from 'react';
const ThemeContext = React.createContext('light');
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button theme={theme}>I am styled by theme context!</button>;
}
useMemo
缓存计算密集型函数的返回值。
当你把一些计算开销较大的值或者组件作为依赖项传递给其他组件或者渲染逻辑时,使用 useMemo 可以避免不必要的计算,从而提高性能。
import React, { useMemo } from 'react';
function expensiveCalculation(num) {
console.log('Calculating...');
for (let i = 0; i < 1000000; i++) {} // 模拟一个耗时计算
return num * 2;
}
function MyComponent({ a, b }) {
const memoizedValue = useMemo(() => {
return expensiveCalculation(a);
}, [a]); // 只有 a 改变时,才会重新计算
return (
<div>
{memoizedValue}
<p>Dependency: {b}</p>
</div>
);
}
useCallback
缓存函数。
当你将一个函数传递给子组件或者作为 props 传递时,使用 useCallback 可以避免不必要的渲染,从而提高性能。
import React, { useCallback, useState } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
const handleIncrement = useCallback(() => {
setCount((c) => c + 1);
}, []); // 由于 setCount 是稳定的,所以这里不需要依赖项
const handleDecrement = useCallback(() => {
setCount((c) => c - 1);
}, []); // 同上
return (
<div>
<button onClick={handleIncrement}>Increment</button>
<button onClick={handleDecrement}>Decrement</button>
<p>Count: {count}</p>
</div>
);
}
useEffect
允许在函数组件中执行副作用操作,如数据获取、订阅或手动更改 React 组件中的 DOM。
import React, { useState, useEffect } from 'react';
function ExampleComponent() {
useEffect(() => {
// 这个函数会在组件每次渲染后执行
// 可以在这里执行副作用操作
return () => {
// 这个清理函数会在组件卸载前执行
};
}, []); // 空数组表示这个 effect 只会在组件挂载时执行一次
return <div>Hello, world!</div>;
}
组件通信
Props(属性)
最常见的方式是通过props属性将数据从父组件传递给子组件。父组件可以在子组件的标签中传递props,子组件通过props接收数据。
// ParentComponent.js
import ChildComponent from './ChildComponent';
function ParentComponent() {
const data = 'Hello from parent';
return <ChildComponent data={data} />;
}
// ChildComponent.js
function ChildComponent(props) {
return <div>{props.data}</div>;
}
Ref(引用)
可以使用ref来直接访问子组件的实例,并通过实例方法传递数据。
// ParentComponent.js
import React, { useRef } from 'react';
import ChildComponent from './ChildComponent';
function ParentComponent() {
const childRef = useRef();
const sendDataToChild = () => {
childRef.current.handleData('Hello from parent');
};
return (
<>
<ChildComponent ref={childRef} />
<button onClick={sendDataToChild}>Send Data</button>
</>
);
}
// ChildComponent.js
import React, { forwardRef, useImperativeHandle } from 'react';
const ChildComponent = forwardRef((props, ref) => {
const handleData = (data) => {
console.log(data);
};
useImperativeHandle(ref, () => ({
handleData
}));
return <div>Child Component</div>;
});
export default ChildComponent;
Context(上下文)
Context允许在组件树中传递数据,而不必一级一级手动传递props。适用于跨多层级的组件传递数据。
// DataContext.js
import React from 'react';
const DataContext = React.createContext();
export default DataContext;
// ParentComponent.js
import DataContext from './DataContext';
import ChildComponent from './ChildComponent';
function ParentComponent() {
const data = 'Hello from parent';
return (
<DataContext.Provider value={data}>
<ChildComponent />
</DataContext.Provider>
);
}
// ChildComponent.js
import DataContext from './DataContext';
function ChildComponent() {
return (
<DataContext.Consumer>
{data => <div>{data}</div>}
</DataContext.Consumer>
);
}
State(状态)
通过组件的状态来传递数据。父组件可以将数据存储在自身的状态中,然后通过props将数据传递给子组件。
// ParentComponent.js
import ChildComponent from './ChildComponent';
function ParentComponent() {
const [data, setData] = useState('Hello from parent');
return <ChildComponent data={data} />;
}
// ChildComponent.js
function ChildComponent(props) {
return <div>{props.data}</div>;
}
回调函数
父组件可以通过回调函数将数据传递给子组件。子组件可以调用回调函数来传递数据给父组件。
// ParentComponent.js
import ChildComponent from './ChildComponent';
function ParentComponent() {
const handleData = (data) => {
console.log(data);
};
return <ChildComponent sendData={handleData} />;
}
// ChildComponent.js
function ChildComponent(props) {
const sendDataToParent = () => {
props.sendData('Hello from child');
};
return <button onClick={sendDataToParent}>Send Data</button>;
}
Event Bus(事件总线)
通过事件总线来进行组件间的通信,一个组件发送事件,另一个组件监听事件并做出相应处理。
// EventBus.js
import { EventEmitter } from 'events';
const eventBus = new EventEmitter();
export default eventBus;
// ParentComponent.js
import eventBus from './EventBus';
import ChildComponent from './ChildComponent';
function ParentComponent() {
const sendDataToChild = () => {
eventBus.emit('sendData', 'Hello from parent');
};
return (
<>
<ChildComponent />
<button onClick={sendDataToChild}>Send Data</button>
</>
);
}
// ChildComponent.js
import React, { useEffect } from 'react';
import eventBus from './EventBus';
function ChildComponent() {
useEffect(() => {
eventBus.on('sendData', (data) => {
console.log(data);
});
return () => {
eventBus.off('sendData');
};
}, []);
return <div>Child Component</div>;
}
Custom Hooks(自定义钩子)
通过自定义钩子来共享逻辑和状态,并在不同组件中使用。可以将数据存储在自定义钩子中,然后在需要的组件中使用。
// UseData.js
import { useState } from 'react';
const useData = () => {
const [data, setData] = useState('Hello from custom hook');
return { data, setData };
};
export default useData;
// ParentComponent.js
import useData from './UseData';
import ChildComponent from './ChildComponent';
function ParentComponent() {
const { data } = useData();
return <ChildComponent data={data} />;
}
// ChildComponent.js
function ChildComponent(props) {
return <div>{props.data}</div>;
}
Redux
在React中结合Redux进行状态管理,通常需要使用
react-redux
库来连接Redux Store和React组件。
- 创建Redux Store:
// store.js
import { createStore } from 'redux';
import rootReducer from './reducers';
const store = createStore(rootReducer);
export default store;
- 定义Reducer:
// reducers.js
const initialState = {
count: 0
};
const counterReducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
case 'DECREMENT':
return { ...state, count: state.count - 1 };
default:
return state;
}
};
export default counterReducer;
- 连接Redux Store和React组件:
// App.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
const App = () => {
const count = useSelector(state => state.count);
const dispatch = useDispatch();
const increment = () => {
dispatch({ type: 'INCREMENT' });
};
const decrement = () => {
dispatch({ type: 'DECREMENT' });
};
return (
<div>
<h1>Count: {count}</h1>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
};
export default App;
- Provider组件:在根组件中使用Provider组件将Redux Store传递给整个应用程序。
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store';
import App from './App';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
状态管理还有很多其他的方式(特别卷)。比如: Zustand、MobX等等
生命周期
挂载(Mounting)、更新(Updating)和卸载(Unmounting)的过程。
React 17 之前:
挂载阶段(Mounting):
constructor()
: 构造函数,在组件被创建时调用,用于初始化state、可以接收一个父组件传来的props、绑定事件处理方法。componentWillMount(已弃用)
: 组件将要挂载render()
: 在这里面我们会写一些html标签及自定义的函数,render执行完后便会将这些语句对应渲染到浏览器上面。componentDidMount()
: 组件挂载后调用,通常用于进行异步数据获取、订阅事件等操作。
更新阶段(Updating):
componentWillReceiveProps(已弃用)
: 在组件接收到新的 props 之前调用。shouldComponentUpdate()
: 决定是否重新渲染组件,在接收新props或state时调用,用于性能优化。 当更新state值的时候会执行这个函数,比如this.setState({})。componentWillUpdate(已弃用)
: 会在组件接收到新的 props 或 state 并即将重新渲染之前被调用。render()
: 和初始化时候执行的那个render一样,只是这里是更新值的,所以dom节点会重新更新一下。componentDidUpdate()
: 组件更新后调用,通常用于处理DOM操作、网络请求等。
卸载阶段(Unmounting):
componentWillUnmount()
: 组件卸载前调用,用于清理定时器、取消订阅等操作。
React 17 之后:
挂载阶段(Mounting):
constructor()
static getDerivedStateFromProps
: React 会在初始挂载和后续更新时调用 render 之前调用它。它应该返回一个对象来更新 state,或者返回 null 就不更新任何内容。挂载阶段只调用一次,用于初始化 state。render()
componentDidMount()
更新阶段(Updating):
static getDerivedStateFromProps
: 更新阶段每次组件接收到新的 props 时都会被调用。shouldComponentUpdate()
render()
getSnapshotBeforeUpdate
: 会在 React 更新 DOM 之前时直接调用它。它使你的组件能够在 DOM 发生更改之前捕获一些信息(例如滚动的位置)。此生命周期方法返回的任何值都将作为参数传递给 componentDidUpdate。componentDidUpdate()
卸载阶段(Unmounting):
componentWillUnmount()
除了上述生命周期方法,还有componentDidCatch()
用于捕获组件树中的错误。此外,React Hooks也提供了函数式组件中的生命周期替代方案,如useEffect()
用于处理副作用。
- componentDidCatch:
import React, { Component } from 'react';
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, info) {
this.setState({ hasError: true });
console.error('Error caught by ErrorBoundary:', error, info);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
export default ErrorBoundary;
- useEffect: 在函数式组件中,可以使用React Hooks来替代类组件中的生命周期方法。
import { useState, useEffect } from 'react';
const FunctionalComponent = () => {
const [count, setCount] = useState(0);
// 相当于 componentDidMount 和 componentDidUpdate
useEffect(() => {
console.log('Component is mounted or updated');
// 清理函数,相当于 componentWillUnmount
return () => {
console.log('Component is about to unmount');
};
}, [count]); // 仅在count发生变化时触发
// 模拟 shouldComponentUpdate
const shouldUpdate = (nextProps, nextState) => {
if (nextState.count !== count) {
return true;
}
return false;
};
const handleClick = () => {
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment Count</button>
</div>
);
};
export default FunctionalComponent;
常用的生命周期方法:
Class 组件中常用:
constructor()
: 用于初始化 state 和绑定方法。render()
: 必须的方法,用于渲染组件。componentDidMount()
: 在组件挂载后被调用,常用于进行 API 调用、订阅等。componentDidUpdate(prevProps, prevState)
: 在组件更新后被调用,可以用于比较 props 和 state 的变化。componentWillUnmount()
: 在组件卸载前被调用,常用于清理资源,如取消订阅、清除定时器等。
Function 组件中使用(Hooks):
useEffect()
: 用于在函数组件中执行副作用操作,类似于 componentDidMount、componentDidUpdate 和 componentWillUnmount。useLayoutEffect()
: 类似于 useEffect,但它会在所有的 DOM 变更之后同步调用 effect,适用于读取 DOM 布局并同步触发重渲染的情况。
不常用的生命周期方法:
Class 组件中不常用:
UNSAFE_componentWillMount()
: 虽然可用,但官方不推荐使用,其用途通常可以在 componentDidMount 中实现。UNSAFE_componentWillReceiveProps(nextProps)
: 虽然可用,但官方不推荐使用,推荐使用 getDerivedStateFromProps 或在 componentDidUpdate 中处理新的 props。UNSAFE_componentWillUpdate(nextProps, nextState)
: 虽然可用,但官方不推荐使用,其用途通常可以在 componentDidUpdate 中实现。
Class 组件中特有的:
getDerivedStateFromProps(props, state)
: 这是一个静态方法,用于根据 props 更新 state,在组件挂载和更新时都会被调用