Open vivipure opened 2 years ago
useState
可以传入一个初始值,然后返回一个值和一个改变值方法的数组
const [count, setCount] = useState(0)
使用 setMethod
会触发组件进行更新,组件函数会重新执行。
这里需要注意:
我们在组件中请求数据,更改DOM或者其他的操作逻辑,都是 side effect . 这些 effect 会影响到其他组件,而且在组件渲染时无法正常完成。
useEffect -- 副作用的hook。每当我们更改数据,或者组件状态变化时想要进行特定的逻辑,都可以使用 useEffect.
useEffect 拥有两个参数,一个是副作用的回调,一个是依赖项。每当依赖项更改时,副作用回调都会重新执行
useEffect 的依赖传入不同的值会有不同的作用。相当于 Class 组件的 componentDidMount, componentDidUpdate, componentWillUnmount.
注意点:
当我们开发时有安装 lint 时,如果 useEffect 传空数组会进行格式提醒的。但是业务场景只需要组件挂载时才执行,而不是每次更新, 这样很容易导致死循环。这个时候我们需要结合其他 hook 一起使用。
使用 useContext 可以让我们使用 createContext 中定义Context。
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>
);
}
useContext(MyContext) 会在当前组件的祖先组件中,找到离自己最近的MyContext.Provider, 获取其中的值,没有则使用默认值。
每当组件使用的context 的值变化时,组件就会重新渲染。
相比于 Class 组件,useContext 可以不需要使用 MyContext.consumer 来获取值。
在简单的业务场景,可以通过这个 hook 进行状态管理,推荐使用 unstated-next 来增强 context 的能力
const [state, dispatch] = useReducer(reducer, initialArg, init);
传入 reducer和初始化的值,第三个参数式很少使用,用来返回默认值,可以根据使用场景来处理默认值
返回状态和 dispacth 函数,可以通过 dispatch reducer中定义操作来更改状态
function init(initialCount) {
return {count: initialCount};
}
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
case 'reset':
return init(action.payload);
default:
throw new Error();
}
}
function Counter({initialCount}) {
const [state, dispatch] = useReducer(reducer, initialCount, init);
return (
<>
Count: {state.count}
<button
onClick={() => dispatch({type: 'reset', payload: initialCount})}>
Reset
</button>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
需要注意的是, 和 reducx 使用一样,更改值时不能直接对引用进行更改,应该返回一个新的状态。
useReducer 可以和 useContext 一起处理比较复杂的业务状态,进行状态管理。
这两个 hook 都是返回一个缓存的值,当依赖值更新时才会进行更新.
useMemo 返回的值为传入回调的返回值, useCallback 返回值为传入的回调
useCallback(fn, [...deps])
等同于 useMemo(() => fn,[...deps])
在 useEffect 中我们提到 useEffect 使用外部定义函数时,空数组会被 linter 警告。这种情况时,我们可以使用 useCallback 定义函数,提供给 useEffect 使用
useMemo 可以理解为 Vue 中的 computed.
const refContainer = useRef(initialValue);
useRef 是一个特殊的hook, 它返回一个包含 current 属性的对象。
useRef 的值初始化后会永远存在,组件刷新不会更改当前的值,常用于访问 dom
const divEl = useRef(null);
...
<div ref={divEl}></div>
同时 ref的返回值更改后不会触发 组件的刷新,我们可以定义一些不需要组件刷新的数据。
useImperativeHandle(ref, createHandle, [deps])
useImperativeHandle 用于子组件暴露方法给父组件使用, 同时可以定义依赖项,当依赖项改变后,handle 中暴露的属性也会变化。
useImperativeHandle 需要结合 forwardRef 一起使用
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
useImperativeHandle 主要是为了避免将 ref 透传,即子组件使用父组件定义的ref 绑定dom.
useLayoutEffect 和 useEffect 的确有相似之处,但是区别也比较大。
区别:
一些dom渲染的操作可以放在 useLayoutEffect 中执行,避免异步执行造成界面闪烁。
useLayoutEffect 会阻塞页面渲染,所以尽量优先使用 useEffect
在SSR 模式 useLayoutEffect 可能会导致实际渲染的内容和服务端首屏渲染的内容不一致,所以尽量避免在 SSR 模式使用该hook
useDebug 用于在 React Devtools 将所在hook 作为 label 进行展示
useDeferredValue 用于延迟值的获取,降低优先级
const deferredValue = useDeferredValue(value);
例如我们在使用筛选时,输入变化时会更新列表。如果列表的数据量较多时渲染可能就会阻塞交互,导致输入卡顿。
当我们使用 useDeferredValue 将值进行包裹后,列表渲染的值会有延迟,保证不会阻塞输入的交互。这个和防抖有点像,但是这个hook 更多是在当前组件空闲时进行渲染,在特定的场景还是比较有用的。
const [isPending, startTransition] = useTransition();
useTransition 提供一个状态和一个handler. 我们可以在handle 中执行数据的变化。
当数据 setState 时,isPending 会变为true, 数据异步修改完成后,isPending会变为false。
可以使用 useTransition 处理一些加载的逻辑
function App() {
const [isPending, startTransition] = useTransition();
const [count, setCount] = useState(0);
function handleClick() {
startTransition(() => {
setCount(c => c + 1);
})
}
return (
<div>
{isPending && <Spinner />}
<button onClick={handleClick}>{count}</button>
</div>
);
}
useId
useId 是
React 18
新增的hooks
, 可以生成独一无二的id。当我们的组件html 里面包含 id 时,多个组件同时渲染,会表现为多个元素拥有同一个id,导致语法上不合理。虽然我们可以用随机数或者时间戳的方式生成id, 但是在函数组件中每次更新都会更改id,所以官方出了
useId
.官方的文档也说明了
useId
出现的作用具体使用:
这里比较有意思的是,生成的 id 并不是合法的 selector id, 也就是说无法通过
DOM
匹配都该元素,毕竟React
推荐使用ref
来操作操作DOM