如果你感觉很累,那说明你在走上坡路 -->
# 注意点
- Hook 是向下兼容的,类组件中不可以使用
- 只能在函数最外层调用 Hook。不要在循环、条件判断或者子函数中调用。(有 lint 插件可以校验)
- 只能在 React 的函数组件中调用 Hook。不要在其他 JavaScript 函数中调用。
# 官方 Hook API 文档
https://zh-hans.reactjs.org/docs/hooks-reference.html (opens new window)
# useState
useState 会返回一对值:当前状态和一个让你更新它的函数,你可以在事件处理函数中或其他一些地方调用这个函数。它类似 class 组件的 this.setState,useState 唯一的参数就是初始 state
const [count, setCount] = useState(0);
如果初始 state 需要通过复杂计算获得,则可以传入一个函数
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});
# useEffect
当你调用 useEffect 时,就是在告诉 React 在完成对 DOM 的更改后运行你的“副作用”函数。由于副作用函数是在组件内声明的,所以它们可以访问到组件的 props 和 state
和 componentDidMount、componentDidUpdate 和 componentWillUnmount 具有相同的用途,只不过被合并成了一个 API。
import React, { useState, useEffect } from "react";
function Example() {
const [count, setCount] = useState(0);
// 相当于 componentDidMount 和 componentDidUpdate:
useEffect(() => {
// 使用浏览器的 API 更新页面标题
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
useEffect 还可以通过返回一个函数来指定如何“清除”副作用,类似于在 componentWillUnmount 干的事情
# useEffect 第二个参数
import * as React from "react";
const App: React.FC = () => {
const [count, setCount] = React.useState(0);
React.useEffect(() => {
console.log("effect");
return () => {
console.log("returnFunction");
};
}, [count]);
return (
<>
<div>{count}</div>
<button onClick={() => setCount(count + 1)}>count++</button>
</>
);
};
export default App;
- 空,则每次 componentDidUpdate 时都会 先触发 returnFunction(如果存在),再触发 effect
- [] 模拟 componentDidMount, returnFunction 模拟 componentWillUnMount
- [id] 仅在 id 的值发生变化以后触发, 先触发 returnFunction(如果存在),再触发 effect
# useContext
定义:const value = useContext(MyContext);
使用:<MyContext.Provider>
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
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
返回一个有缓存的函数,类似函数柯里化.useCallback(fn, deps) 相当于 useMemo(() => fn, deps)
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
# useMemo
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算
# useRef
useRef 返回一个可变的 ref 对象
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>
</>
);
}
# useImperativeHandle
# useLayoutEffect
# useDebugValue
# 自定义 Hook
自定义 Hook 更像是一种约定而不是功能。如果函数的名字以 “use” 开头并调用其他 Hook,我们就说这是一个自定义 Hook。 useSomething 的命名约定可以让我们的 linter 插件在使用 Hook 的代码中找到 bug。