Appearance
React Hooks
1. 简介Hooks
Hooks 在2018年10月推出,目的是解决React的状态共享以及组件生命周期管理混乱的问题。
以 use 开头的函数被称为 Hook。useState 是 React 提供的一个内置 Hook。 你可以在 React API 参考 中找到其他内置的 Hook。你也可以通过组合现有的 Hook 来编写属于你自己的 Hook。
React 内置 Hook 文档
2. useState 状态
useState函数是React自带的一个Hook函数,而Hook函数拥有React状态和生命周期管理的能力。 可以看到,useState函数的入参只有一个,就是state的初始值,这个初始值可以是数字、字符串、对象,甚至是一个函数,如下所示。
jsx
function Example (props) {
const [ count, setCount ] = useState(() => {
return props.count || 0
})
return (
<div>
You clicked : { count }
<button onClick={() => { setCount(count + 1)}}>
Click me {count}
</button>
</div>
)
}3. useEffect 副作用
什么是副作用呢,就是除了状态相关的逻辑,比如网络请求,监听事件,查找 dom
例如 组件在状态更新的时候改变 document.title
jsx
function App () {
const [ count, setCount ] = useState(0)
useEffect(() => {
document.title = count
})
return (
<div>
页面名称: { count }
<button onClick={() => { setCount(count + 1 )}}>点我</button>
</div>
)
}4. useContext 上下文对象
useContext 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。设计的目的就是解决组件树间数据传递的问题。
- 用法
jsx
import React, { useContext, useState } from 'react';
const MyThemeContext = React.createContext({theme: 'light'}); // 创建一个上下文
function MyComponent() {
const themeContext = useContext(MyThemeContext); // 使用上下文
return (<div>{themeContext.theme}</div>);
}- 19 版本演示
jsx
import React, { useContext, useState } from 'react';
const ThemeContext = React.createContext<ThemeContextType>({} as ThemeContextType);
interface ThemeContextType {
theme: string;
setTheme: (theme: string) => void;
}
const Child = () => {
const themeContext = useContext(ThemeContext);
const styles = {
backgroundColor: themeContext.theme === 'light' ? 'white' : 'black',
border: '1px solid red',
width: 100 + 'px',
height: 100 + 'px',
color: themeContext.theme === 'light' ? 'black' : 'white'
}
return <div>
<div style={styles}>
child
</div>
</div>
}
const Parent = () => {
const themeContext = useContext(ThemeContext);
const styles = {
backgroundColor: themeContext.theme === 'light' ? 'white' : 'black',
border: '1px solid red',
width: 100 + 'px',
height: 100 + 'px',
color: themeContext.theme === 'light' ? 'black' : 'white'
}
return <div>
<div style={styles}>
Parent
</div>
<Child />
</div>
}
function App() {
const [theme, setTheme] = useState('light');
return (
<div>
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>切换主题</button>
<ThemeContext value={{ theme, setTheme }}>
<Parent />
<ThemeContext>
</div >
);
}
export default App;5. useRef 获取组件实例或者DOM元素
类似 vue3 ref
jsx
function TextInputWithFocusButton() {
const inputEl = useRef(); /
const onButtonClick = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}6. useReducer 处理复杂状态
与useState相似,useReducer也是 React 的 Hook,而且也只能放在组件最顶层使用。与前者不同的地方在于,它是通过 action 来更新状态的,使状态更新逻辑更具可读性。
jsx
const [state, dispatch] = useReducer(reducer, initialArg, init?)useReducer接收三个参数:
- reducer 函数:指定如何更新状态的还原函数,它必须是纯函数,以 state 和 dispatch 为参数,并返回下一个状态。
- initialArg 初始状态:初始状态的计算值。
- init(可选的)初始化参数:用于返回初始状态。如果未指定,初始状态将设置为 initialArg;如果有指定,初始状态将被设置为调用init(initialArg)的结果。
useReducer返回两个参数:
- state 当前的状态:当前状态。在第一次渲染时,它会被设置为init(initialArg)或 initialArg(如果没有 init 的情况下)。
- dispatch:调度函数,用于调用 reducer 函数,以更新状态并触发重新渲染。
示例
jsx
import React, { useReducer } from 'react';
const initialState = { count: 0 };
// Reducer 函数接收当前状态和动作,返回新的状态
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>
</>
);
}7. useMemo 性能优化
useMemo接受两个参数:- 一个函数,这个函数返回需要记住的值。
- 一个依赖项数组,当数组中的依赖项发生变化时,才会重新计算函数的返回值。
jsx
import React, { useMemo } from 'react';
function App() {
const [count, setCount] = React.useState(0);
const expensiveCalculation = useMemo(() => {
console.log('计算中...');
return count * 2;
}, [count]);
return (
<div>
<p>结果:{expensiveCalculation}</p>
<button onClick={() => setCount(count + 1)}>增加</button>
</div>
);
}
export default App;在这个例子中,我们使用 useMemo 对 count * 2 这个计算进行了优化。只有当 count 发生变化时,expensiveCalculation 的值才会重新计算。
8. useCallback 性能优化
useCallback接受两个参数:- 一个函数,这个函数是我们需要记住的函数。
- 一个依赖项数组,当数组中的依赖项发生变化时,才会重新创建新的函数。
jsx
import React, { useState, useCallback } from 'react';
function App() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log('点击了按钮');
setCount(count + 1);
}, [count]);
return (
<div>
<p>点击次数:{count}</p>
<button onClick={handleClick}>增加</button>
</div>
);
}
export default App;只有当 count 发生变化时,handleClick 函数才会被重新创建。