生命的意义在于折腾

react的10个hooks

基础 Hook

useState

js

1/* 简单初始化一个state */
2const [data, setData] = React.useState({name:'age',age:22});
3// initialState type [ bool, number, string, object, array, .... ]
4// data : 返回一个值 初始化 和 initialState 相同
5// setData : 修改state的函数  setState(initialState)
6  
7/* 惰性初始 state */
8const [state, setState] = useState(() => {
9    const initialState = props.list.filter(item=>item>2);
10    return initialState;
11});
12

注意

与 class 组件中的 setState 方法不同,useState 不会自动合并更新对象。你可以用函数式的 setState 结合展开运算符来达到合并更新对象的效果。默认情况下,effect 将在每轮渲染结束后执行,但你可以选择让它 在只有某些值改变的时候 才执行。

js

1setData(prevData => {
2  // 也可以使用 Object.assign
3  return {...prevData, ...updatedValues};
4});
5setData({...data,...updatedValues})
6

useEffect

该 Hook 接收一个包含命令式、且可能有副作用代码的函数。赋值给 useEffect 的函数会在组件渲染到屏幕之后执行。

javascript

1	// 用法一 (可重复执行)
2  React.useEffect(()=>{
3  	console.log('页面render发生变化了')
4  })

js

1	// 用法二 (执行一次)
2  React.useEffect(()=>{
3  	return ()=>{
4    	console.log('页面组件卸载了')
5    }
6  })

javascript

1	// 用法三 (执行一次)
2  React.useEffect(()=>{
3  	console.log('页面render结束后')
4  },[])

javascript

1	// 用法四 (可重复执行)
2  React.useEffect(()=>{
3  	console.log('监听isLoading发生变化')
4  },[isLoading])

useContext

接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider> value prop 决定。

javascript

1const themes = {
2  light: {
3    foreground: "#000000",
4    background: "#eeeeee"
5  },
6  dark: {
7    foreground: "#ffffff",
8    background: "#222222"
9  }
10};
11
12const ThemeContext = React.createContext(themes.light);
13
14function App() {
15  return (
16    <ThemeContext.Provider value={themes.dark}>
17      <Toolbar />
18    </ThemeContext.Provider>
19  );
20}
21
22function Toolbar(props) {
23  return (
24    <div>
25      <ThemedButton />
26    </div>
27  );
28}
29
30function ThemedButton() {
31  const theme = useContext(ThemeContext);
32  return (
33    <button style={{ background: theme.background, color: theme.foreground }}>
34      I am styled by theme context!
35    </button>
36  );
37}

额外的 Hook

useReducer

javascript

1
2const [state, dispatch] = useReducer(reducer, initialArg, init);
3

useState 的替代方案。它接收一个形如 (state, action) => newStatereducer,并返回当前的 state 以及与其配套的 dispatch 方法。

在某些场景下,useReducer 会比 useState 更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。并且,使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为你可以向子组件传递 dispatch 而不是回调函数 。

jsx

1
2const initialState = {count: 0};
3
4function reducer(state, action) {
5  switch (action.type) {
6    case 'increment':
7      return {count: state.count + 1};
8    case 'decrement':
9      return {count: state.count - 1};
10    default:
11      throw new Error();
12  }
13}
14
15function Counter() {
16  const [state, dispatch] = useReducer(reducer, initialState);
17  return (
18    <>
19      Count: {state.count}
20      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
21      <button onClick={() => dispatch({type: 'increment'})}>+</button>
22    </>
23  );
24}
25

惰性初始化

你可以选择惰性地创建初始 state。为此,需要将 init 函数作为 useReducer 的第三个参数传入,这样初始 state 将被设置为 init(initialArg)

javascript

1function init(initialCount) {
2  return {count: initialCount};
3}
4
5function reducer(state, action) {
6  switch (action.type) {
7    case 'increment':
8      return {count: state.count + 1};
9    case 'decrement':
10      return {count: state.count - 1};
11    case 'reset':
12      return init(action.payload);
13    default:
14      throw new Error();
15  }
16}
17
18function Counter({initialCount}) {
19  const [state, dispatch] = useReducer(reducer, initialCount, init);
20  return (
21    <>
22      Count: {state.count}
23      <button
24        onClick={() => dispatch({type: 'reset', payload: initialCount})}>
25        Reset
26      </button>
27      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
28      <button onClick={() => dispatch({type: 'increment'})}>+</button>
29    </>
30  );
31}
32

useCallback

把内联回调函数及依赖项数组作为参数传入 useCallback,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。当你把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate)的子组件时,它将非常有用。

javascript

1const memoizedCallback = useCallback(
2  () => {
3    doSomething(a, b);
4  },
5  [a, b],
6);

useMemo

把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。

记住,传入 useMemo 的函数会在渲染期间执行。请不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于 useEffect 的适用范畴,而不是 useMemo。

如果没有提供依赖项数组,useMemo 在每次渲染时都会计算新的值。

你可以把 useMemo 作为性能优化的手段,但不要把它当成语义上的保证。将来,React 可能会选择“遗忘”以前的一些 memoized 值,并在下次渲染时重新计算它们,比如为离屏组件释放内存。先编写在没有 useMemo 的情况下也可以执行的代码 —— 之后再在你的代码中添加 useMemo,以达到优化性能的目的。

注意

依赖项数组不会作为参数传给“创建”函数。虽然从概念上来说它表现为:所有“创建”函数中引用的值都应该出现在依赖项数组中。未来编译器会更加智能,届时自动创建数组将成为可能。

javascript

1const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

useRef

useRef()ref 属性更有用。它可以很方便地保存任何可变值,其类似于在 class 中使用实例字段的方式。

这是因为它创建的是一个普通 Javascript 对象。而 useRef() 和自建一个 {current: ...} 对象的唯一区别是,useRef 会在每次渲染时返回同一个 ref 对象。

请记住,当 ref 对象内容发生变化时,useRef 并不会通知你。变更 .current 属性不会引发组件重新渲染。如果想要在 React 绑定或解绑 DOM 节点的 ref 时运行某些代码,则需要使用回调 ref 来实现。

javascript

1function TextInputWithFocusButton() {
2  const inputEl = useRef(null);
3  const onButtonClick = () => {
4    // `current` 指向已挂载到 DOM 上的文本输入元素
5    inputEl.current.focus();
6  };
7  return (
8    <>
9      <input ref={inputEl} type="text" />
10      <button onClick={onButtonClick}>Focus the input</button>
11    </>
12  );
13}

useImperativeHandle

useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值。在大多数情况下,应当避免使用 ref 这样的命令式代码。useImperativeHandle 应当与 forwardRef 一起使用:

javascript

1function FancyInput(props, ref) {
2  const inputRef = useRef();
3  useImperativeHandle(ref, () => ({
4    focus: () => {
5      inputRef.current.focus();
6    }
7  }));
8  return <input ref={inputRef} ... />;
9}
10FancyInput = forwardRef(FancyInput);

useLayoutEffect

其函数签名与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。

尽可能使用标准的 useEffect 以避免阻塞视觉更新。

提示

如果你正在将代码从 class 组件迁移到使用 Hook 的函数组件,则需要注意 useLayoutEffect 与 componentDidMount、componentDidUpdate 的调用阶段是一样的。但是,我们推荐你一开始先用 useEffect,只有当它出问题的时候再尝试使用 useLayoutEffect。

如果你使用服务端渲染,请记住,无论 useLayoutEffect 还是 useEffect 都无法在 Javascript 代码加载完成之前执行。这就是为什么在服务端渲染组件中引入 useLayoutEffect 代码时会触发 React 告警。解决这个问题,需要将代码逻辑移至 useEffect 中(如果首次渲染不需要这段逻辑的情况下),或是将该组件延迟到客户端渲染完成后再显示(如果直到 useLayoutEffect 执行之前 HTML 都显示错乱的情况下)。

若要从服务端渲染的 HTML 中排除依赖布局 effect 的组件,可以通过使用 showChild && <Child /> 进行条件渲染,并使用 useEffect(() => { setShowChild(true); }, []) 延迟展示组件。这样,在客户端渲染完成之前,UI 就不会像之前那样显示错乱了。

javascript

1useLayoutEffect(()=>{
2	console.log(document.getElementById('app'))
3},[])

useDebugValue

useDebugValue 可用于在 React 开发者工具中显示自定义 hook 的标签。 在某些情况下,格式化值的显示可能是一项开销很大的操作。除非需要检查 Hook,否则没有必要这么做。

因此,useDebugValue 接受一个格式化函数作为可选的第二个参数。该函数只有在 Hook 被检查时才会被调用。它接受 debug 值作为参数,并且会返回一个格式化的显示值。

例如,一个返回 Date 值的自定义 Hook 可以通过格式化函数来避免不必要的 toDateString 函数调用:

javascript

1useDebugValue(date, date => date.toDateString());
阅读量:1954发布日期:2021-07-26 17:31:20

博客描述

本文详细介绍react的10个hooks,有兴趣的同学可以一起留言交流

留言板