• react中的那些Hooks的总结


    1、useState

    useState通过在函数组件里调用它来给组件添加一些内部state。

    React会在重复渲染时保留这个state

    useState会返回一对值:当前状态和一个让你更新它的函数,你可以在事件处理函数中或其他一些地方调用这个函数。它类似class组件的this.setState,但是它不会把新的state和旧的state合并

    import React, { useState } from "react";
    function App() {
      const [obj, setObject] = useState({
        count: 0,
        name: "alife"
      });
      return (
        <div className="App">
          Count: {obj.count}
          <button onClick={() => setObject({ ...obj, count: obj.count + 1 })}>+</button>
          <button onClick={() => setObject({ ...obj, count: obj.count - 1 })}>-</button>
        </div>
      );
    }

    2、useEffect

    useEffect用来帮助开发者处理函数的副作用

    import React, { useState, useEffect } from "react";
    let timer = null;
    function App() {
      const [count, setCount] = useState(0);
      useEffect(() => {
        document.title = "componentDidMount" + count;
      },[count]);
     
      useEffect(() => {
        timer = setInterval(() => {
          setCount(prevCount => prevCount + 1);
        }, 1000);
        // 一定注意下这个顺序:
        // 告诉react在下次重新渲染组件之后,同时是下次执行上面setInterval之前调用
        return () => {
          document.title = "componentWillUnmount";
          clearInterval(timer);
        };
      }, []);
      return (
        <div>
          Count: {count}
          <button onClick={() => clearInterval(timer)}>clear</button>
        </div>
      );
    }

    useEffect 第一个参数接收一个函数,可以用来做一些副作用比如异步请求,修改外部参数等行为,而第二个参数称之为dependencies,是一个数组,如果数组中的值变化才会触发 执行useEffect 第一个参数中的函数。返回值(如果有)则在组件销毁或者调用函数前调用

    • 1.比如第一个 useEffect 中,理解起来就是一旦 count 值发生改变,则修改 documen.title 值;
    • 2.而第二个 useEffect 中传递了一个空数组[],这种情况下只有在组件初始化或销毁的时候才会触发,用来代替 componentDidMount 和 componentWillUnmount,慎用;
    • 3.还有另外一个情况,就是不传递第二个参数,也就是useEffect只接收了第一个函数参数,代表不监听任何参数变化。每次渲染DOM之后,都会执行useEffect中的函数。

    基于useEffect封装的生命周期函数(componentDidUpdate)

    function useUpdate(fn) {
        // useRef 创建一个引用
        const mounting = useRef(true);
        useEffect(() => {
          if (mounting.current) {
            mounting.current = false;
          } else {
            fn();
          }
        });
    }

    3、useContext

    useContext是用来处理多层级传递数据的方式。

    const colorContext = React.createContext("gray");
    function Bar() {
      const color = useContext(colorContext);
      return <div>{color}</div>;
    }
    function Foo() {
      return <Bar />;
    }
    function App() {
      return (
        <colorContext.Provider value={"red"}>
          <Foo />
        </colorContext.Provider>
      );
    }

    使用context解决了Provider和Consumer的多层嵌套的问题

    4、useReducer

    useReducer这个hooks在使用上几乎跟Redux/React-Redux一模一样,唯一缺少的就是无法使用redux提供的中间件

    而且跟Redux基本上是一致的,用法也很简单,算是提供一个mini的Redux版本

    import React, { useReducer } from "react";
    const initialState = {
      count: 0
    };
    function reducer(state, action) {
      switch (action.type) {
        case "increment":
          return { count: state.count + action.payload };
        case "decrement":
          return { count: state.count - action.payload };
        default:
          throw new Error();
      }
    }
    function App() {
      const [state, dispatch] = useReducer(reducer, initialState);
      return (
        <>
          Count: {state.count}
          <button onClick={() => dispatch({ type: "increment", payload: 5 })}>
            +
          </button>
          <button onClick={() => dispatch({ type: "decrement", payload: 5 })}>
            -
          </button>
        </>
      );
    }

    4、useCallback

    function App() {
      const memoizedHandleClick = useCallback(() => {
        console.log('Click happened')
      }, []); // 空数组代表无论什么情况下该函数都不会发生改变
      return <SomeComponent onClick={memoizedHandleClick}>Click Me</SomeComponent>;
    }

    第二个参数传入一个数组,数组中的每一项一旦值或者引用发生改变,useCallback就会重新返回一个新的记忆函数提供给后面渲染

    5、useMemo

    useMemo的功能和useCallback是相似的,如果你想通过使用useMemo返回一个记忆函数也是完全可以的

    可以改写用useCallback写过的例子:

    function App() {
      const memoizedHandleClick = useMemo(() => () => {
        console.log('Click happened')
      }, []); // 空数组代表无论什么情况下该函数都不会发生改变
      return <SomeComponent onClick={memoizedHandleClick}>Click Me</SomeComponent>;
    }

    唯一的区别是:useCallback不会执行第一个参数函数,而是将它返回给你,而useMemo会执行第一个函数并且将执行结果返回给你,所以在前面的例子中,可以返回handleClick来达到存储函数的目的

    所以useCallback常用记忆事件函数,生成记忆后的事件函数并传递给子组件使用。而useMemo适合经过函数计算得到一个确定的值,比如记忆组件

    6、useRef

    useRef跟createRef类似,都可以用来生成DOM对象

    import React, { useState, useRef } from "react";
    function App() {
      let [name, setName] = useState("Nate");
      let nameRef = useRef();
      const submitButton = () => {
        setName(nameRef.current.value);
      };
      return (
        <div className="App">
          <p>{name}</p>
     
          <div>
            <input ref={nameRef} type="text" />
            <button type="button" onClick={submitButton}>
              Submit
            </button>
          </div>
        </div>
      );
    }

    useRef返回的值传递给组件或者DOM的ref属性,就可以通过ref.current值访问组件或真实的DOM节点,重点是组件也可以访问到的,从而可以对DOM进行一些操作,比如监听事件等等

    当然useRef远比你想象中的功能更加强大,useRef的功能有点像类属性,或者说您想要在组件中记录一些值,并且这些值在稍后可以更改

    function App() {
      const [count, setCount] = useState(0);
     
      useEffect(() => {
        setTimeout(() => {
          alert("count: " + count);
        }, 3000);
      }, [count]);
     
      return (
        <div>
          <p>You clicked {count} times</p>
          <button onClick={() => setCount(count + 1)}>增加 count</button>
          <button onClick={() => setCount(count - 1)}>减少 count</button>
        </div>
      );
    }

    先点击增加button,后点击减少button,3秒后先alert 1,后alert 0,而不是alert两次0。这就是所谓的 capture value 的特性。而在类组件中 3 秒后输出的就是修改后的值,因为这时候** message 是挂载在 this 变量上,它保留的是一个引用值**,对 this 属性的访问都会获取到最新的值,类组件举例,在线Demo。讲到这里你应该就明白了,useRef 创建一个引用,就可以有效规避 React Hooks 中 Capture Value 特性。useRef避免 Capture Value 

    function App() {
      const count = useRef(0);
     
      const showCount = () => {
        alert("count: " + count.current);
      };
     
      const handleClick = number => {
        count.current = count.current + number;
        setTimeout(showCount, 3000);
      };
     
      return (
        <div>
          <p>You clicked {count.current} times</p>
          <button onClick={() => handleClick(1)}>增加 count</button>
          <button onClick={() => handleClick(-1)}>减少 count</button>
        </div>
      );
    }

    只要将赋值与取值的对象变成 useRef,而不是 useState,就可以躲过 capture value 特性,在 3 秒后得到最新的值。

    7、useImperativeHandle

    通过 useImperativeHandle 用于让父组件获取子组件内的索引

    import React, { useRef, useEffect, useImperativeHandle, forwardRef } from "react";
    function ChildInputComponent(props, ref) {
      const inputRef = useRef(null);
      useImperativeHandle(ref, () => inputRef.current);
      return <input type="text" name="child input" ref={inputRef} />;
    }
    const ChildInput = forwardRef(ChildInputComponent);
    function App() {
      const inputRef = useRef(null);
      useEffect(() => {
        inputRef.current.focus();
      }, []);
      return (
        <div>
          <ChildInput ref={inputRef} />
        </div>
      );
    }

    8、useLayoutEffect

    使用 useEffect 就可以帮我们处理组件的副作用,但是如果想要同步调用一些副作用,比如对 DOM 的操作,就需要使用 useLayoutEffect,useLayoutEffect 中的副作用会在 DOM 更新之后同步执行。

    function App() {
      const [width, setWidth] = useState(0);
      useLayoutEffect(() => {
        const title = document.querySelector("#title");
        const titleWidth = title.getBoundingClientRect().width;
        console.log("useLayoutEffect");
        if (width !== titleWidth) {
          setWidth(titleWidth);
        }
      });
      useEffect(() => {
        console.log("useEffect");
      });
      return (
        <div>
          <h1 id="title">hello</h1>
          <h2>{width}</h2>
        </div>
      );
    }

    useEffect和useLayoutEffect有什么区别?

    简单来说就是调用时机不同,useLayoutEffect和原来componentDidMount&componentDidUpdate一致,在react完成DOM更新后马上同步调用的代码,会阻塞页面渲染。而useEffect是会在整个页面渲染完才会调用的代码。

    在实际使用时如果想避免页面抖动(在useEffect里修改DOM很有可能出现)的话,可以把需要操作DOM的代码放在useLayoutEffect里。关于使用useEffect导致页面抖动,参考git仓库git仓库示例

    不过useLayoutEffect在服务端渲染时会出现一个warning,要消除的话得用useEffect代替或者推迟渲染时机。

  • 相关阅读:
    获取jsonPath的节点名称
    如何删除 macOS High Sierra 上的 swapfile
    Prototype fake it till make it.观后感
    intellij idea 初步环境熟悉
    一个比较综合的项目--》>图片缓存,下拉刷新等
    写的很好的博客->有关性能优化、图片缓存等
    layout优化之-》viewStub,requestFocus,merge,include
    有关ActionBar
    android:installLocation 解析
    Android开发效率—Eclipse快捷键
  • 原文地址:https://www.cnblogs.com/Roxxane/p/14498470.html
Copyright © 2020-2023  润新知