本文最后更新于:2020-05-11 20:50:17

1.useState

这个就太简单了,不多做介绍

2.useEffect

函数组件能保存状态,但是对于异步请求,副作用的操作还是无能为力,所以 React 提供了 useEffect 来帮助开发者处理函数组件的副作用

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
let timer = null;

function App() {
  const [count, setCount] = useState(0);
  useEffect(() => {
      document.title = "componentDidMount" + count;
    },[count]);

  useEffect(() => {
    timer = setInterval(() => {
      setCount(prevCount => prevCount + 1);
    }, 1000);
    return () => {
      document.title = "componentWillUnmount";
      clearInterval(timer);
    };
  }, []);

  return (
    <div>
      Count: {count}
      <button onClick={() => clearInterval(timer)}>clear</button>
    </div>
  );
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

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

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

3.模拟一个 componentDidUpdate

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

4.useContext

跨组件传值(依赖createContext)
useContext用来处理多层级传递数据的方式,在以前组件树种,跨层级祖先组件想要给孙子组件传递数据的时候,除了一层层 props 往下透传之外,我们还可以使用 React Context API 来帮我们做这件事

//1.从react中导入useContext和createContext
import React, { useContext,createContext } from "react";
import ReactDOM from "react-dom";

const { Provider, Consumer } = createContext(null);

//2.调用createContext 接受一个初始化参数
const colorContext = createContext({
  color: "blue"
});

function Bar() {
  //3.把colorContext传递给useContext 返回值即是想要透传的数据了
  //传递给 useContext 的是 context 而不是 consumer
  const colors = useContext(colorContext);
  return (
    <Consumer>
      {color => (
        <>
          <div>{color}</div>
          <h2>我是用useContext传过来的值 {colors.color}</h2>
        </>
      )}
    </Consumer>
  );
}

function Foo() {
  return <Bar />;
}

function App() {
  return (
    <Provider value={"grey"}>
      <Foo />
    </Provider>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

5.useCallback

常用于记忆事件函数,生成记忆后的事件函数并传递给子组件使用
参考链接:https://juejin.im/post/5dd337985188252a1873730f?utm_source=gold_browser_extension

1.作用是:获得一个记忆后的函数
2.第一个参数接收一个函数,且不会执行第一个参数函数,而是将它返回给你
3.第二个参数传入一个数组,数组中的每一项一旦值或者引用发生改变,useCallback 就会重新返回一个新的记忆函数提供给后面进行渲染。
4.这样只要子组件继承了 PureComponent 或者使用 React.memo 就可以有效避免不必要的 VDOM 渲染。

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

6.useMemo

记忆计算
参考链接:https://juejin.im/post/5dd337985188252a1873730f?utm_source=gold_browser_extension

useCallback 的功能完全可以由 useMemo 所取代,如果你想通过使用 useMemo 返回一个记忆函数也是完全可以的。
useCallback(fn,inputs) <==> useMemo(()=>fn,inputs);

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

useMemo 会执行第一个函数并且将函数执行结果返回给你
useMemo 更适合经过函数计算得到一个确定的值,比如记忆组件。

import React, { useState, useMemo } from "react";
import ReactDOM from "react-dom";

const Counter = props => {
  return <div>{props.count}</div>;
};

function App() {
  const [count, setCount] = useState(0);

  const changeCount = () => {
    setCount(count + 1);
  };

  //useMemo 返回 经过函数计算得到一个确定的值
  const dobuleCount = useMemo(() => count * 2, [count]);

  //useMemo 返回 一个函数
  const changeCount2 = useMemo(() => () => setCount(count + 1), [count]);

  return (
    <div className="App">
      <Counter count={count} />
      <div>dobuleCount:{dobuleCount}</div>
      <button onClick={changeCount}>changeCount:{count}</button>
      <button onClick={changeCount2}>changeCount2:{count}</button>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
import React, { useState, useMemo, memo, useCallback } from "react";
import ReactDOM from "react-dom";

const Counter = memo(props => {
  console.log("counter render");
  return <div onClick={props.onClick}>{props.count}</div>;
});

function App() {
  const [count, setCount] = useState(0);

  const changeCount = () => {
    setCount(count + 1);
  };

  //useMemo 返回 经过函数计算得到一个确定的值
  const dobuleCount = useMemo(() => count * 2, [count === 3]);

  //useMemo 返回 一个函数
  const changeCount2 = useMemo(() => () => setCount(count + 1), [count]);

  //userMemo 返回一个 函数 同时传递给子组件,在子组件里面调用
  // const onClick = useMemo(() => {
  //   return () => {
  //     console.log("click");
  //   };
  // }, []);

  //等价于上面
  // const onClick = useCallback(() => {
  //   console.log("click");
  // }, []);

  const onClick = () => {
    console.log("click");
  };



  return (
    <div className="App">
      <Counter count={dobuleCount} onClick={onClick} />
      <div>dobuleCount:{dobuleCount}</div>
      <button onClick={changeCount}>changeCount:{count}</button>
      <button onClick={changeCount2}>changeCount2:{count}</button>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

总结,当需要通过属性给子组件传递函数(方法)时,这个方法就由useCallback或者useMemo来创建

这个例子,每次点击按钮count都发生改变,在Counter组件中通过memo来优化,只有count改变才渲染。
但是由于我们同时也传递了方法给该组件,就会导致每次count改变 所获取的函数事件句柄重新生成了。
这就导致每次count改变 Counter重新生成事件,要避免就在外面传递函数时,通过useCallback或者useMemo来优化。

const onClick = useCallback(() => {
    console.log("click");
}, []);

const onClick = useMemo(() => {
    return () => {
    console.log("click");
    };
}, []);

技术      react

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!