美文网首页
React Hooks 知识整理

React Hooks 知识整理

作者: VioletJack | 来源:发表于2020-07-21 22:30 被阅读0次

    今天写代码写到 Hook 函数的时候,突然感觉 Hook 函数是真的香。感觉已经回不去了。所以今天顺便整理下一些 Hooks 函数。

    Hooks 的起因

    React 团队希望,组件不要变成复杂的容器,最好只是数据流的管道。开发者根据需要,组合管道即可。 组件的最佳写法应该是函数,而不是类。

    React Hooks 的意思是,组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码"钩"进来。 React Hooks 就是那些钩子。

    useState

    主要就是替代原先的 state 和 setState API。

    • useState() 函数中的参数是初始值。
    • useState() 函数的参数可以是带有返回值的函数。
    • useState() 返回一个数组,数组第一个值类似于 state,第二个值类似于 setState。
    • 可以进行类型定义:const [text, setText] = useState<string>('hello world')

    下面是一个受控输入框的例子:

    import React, { useState } from 'react'
    import { Input } from 'antd'
    
    export default () => {
      const [text, setText] = useState('hello world')
    
      return (
        <div>
          <div>内容是:{text}</div>
          <Input value={text} onChange={(e) => setText(e.target.value)} />
        </div>
      )
    }
    

    useEffect

    useEffect(() => {
      const subscription = props.source.subscribe();
      return () => {
        // 清除订阅
        subscription.unsubscribe();
      };
    }, [value]);
    

    useContext

    接收一个 Context 对象并返回当前 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>
      );
    }
    

    useReducer

    其实就是 redux 的 hook 版本,类似于 useState,但是数据会放到 redux 中。函数有三个参数,分别是 reducer 分发事件,initialCount 初始化数据,init 是初始化事件。返回 redux 当前数据 state 以及 dispatch 函数用于改变 redux 的 state 值。

    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>
        </>
      );
    }
    

    useCallback

    const memoizedCallback = useCallback(
      () => {
        doSomething(a, b);
      },
      [a, b],
    );
    

    将回调函数分成函数和传参传给 useCallback 函数,只有当传参发生变化时,回调函数才会执行。算是对回调函数的一种优化了。

    useCallback(fn, deps) 相当于 useMemo(() => fn, deps)

    useMemo

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

    它一般用于计算,会在参数发生变化是重新计算。它执行在渲染期间,所以不要滥用。它是一种性能优化的方式(感觉类似于是 Vue 的 computed 属性)。

    useCallback 返回优化版的回调函数,而 useMemo 返回优化版的计算数值。或者说,useMemo 返回的是函数运行的结果,useCallback 返回的是函数。

    useRef

    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>
        </>
      );
    }
    

    useRef 返回一个可变的 ref 对象,函数中的参数是它的初始值。这个方法用来获取 ref 操作 DOM 非常好用。算是最新的获取 ref 的方式了。

    useImperativeHandle

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

    function FancyInput(props, ref) {
      const inputRef = useRef();
      useImperativeHandle(ref, () => ({
        focus: () => {
          inputRef.current.focus();
        }
      }));
      return <input ref={inputRef} ... />;
    }
    FancyInput = forwardRef(FancyInput);
    

    在本例中,渲染 <FancyInput ref={inputRef} /> 的父组件可以调用 inputRef.current.focus()

    暴露 ref 给父组件,父组件可以调用 useImperativeHandle 里面定义的函数。

    useLayoutEffect

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

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

    useDebugValue

    useDebugValue 可用于在 React 开发者工具中显示自定义 hook 的标签。它一般用在自定义 Hook 函数时。但是不推荐频繁使用。

    自定义 Hook

    之前,在 React 中有两种流行的方式来共享组件之间的状态逻辑: render props高阶组件,而现在自定义 Hook 也能实现这一点。

    一些自定义 Hook 的点:

    • 函数命名必须使用 use 开头。
    • 自定义 hook 函数本质上还是正常的函数,只是可以在里面使用 Hook API 的东西,在提取和实现逻辑。函数的传参和返回值和其他函数是一样的。
    • 两个组件使用相同的 Hook 是不会共享 state 的!

    感觉用好了自定义 Hook 会让代码更加的优雅整洁。回头可以试着抽一些自定义 Hook 出来。

    其他

    一开始我不理解为什么 Hook 函数通常返回的是一个数组,后来发现是为了方便命名 Hook 函数中的内容,配合上数据解构特别方便。如果返回的是个对象,那么对象名称就被定死了。

    最后

    以上就是我对于 React Hook 的整理笔记和学习笔记,总体上感觉 Hook 函数真的很好用。实际用下来明显感觉代码轻量了很多。按照需要使用 Hook 函数也让写代码更加的自由。而自定义 Hook 也提供了一种很好的共享逻辑抽象方案。值得学习和使用。真香~

    参考资料

    相关文章

      网友评论

          本文标题:React Hooks 知识整理

          本文链接:https://www.haomeiwen.com/subject/cfowkktx.html