# 一.useState

# 1. 定义:

react 自带的一个 hook 函数,它的作用是用来声明状态变量。

# 2. 用法

  • 声明
//数组结构
const [count, setCount] = useState(0);
// 不写成数组解构
// let _useState = userState(0)
// let count = _useState[0]
// let setCount = _useState[1]
1
2
3
4
5
6
  • 读取
<p>You clicked {count} times</p>
1
  • 使用
<button
  onClick={() => {
    setCount(count + 1);
  }}
>
  click me
</button>
1
2
3
4
5
6
7

# 3.多状态声明注意事项

在使用 useState 的时候只赋了初始值,并没有绑定任何的 key,React是根据useState出现的顺序来确定的 React Hooks不能出现在条件判断语句中,因为它必须有完全一样的渲染顺序。

# 二.useEffect

# 1. 定义:

useEffect 代替常用生命周期函数,代表第一次组件渲染和每次组件更新都会执行这个函数

# 2.实现 componentDidMonut 和 componentDidUpdate 例子

import React, { useState, useEffect } from "react";
function Example() {
  const [count, setCount] = useState(0);
  //---关键代码---------start-------
  useEffect(() => {
    console.log(`useEffect=>You clicked ${count} times`);
  });
  //---关键代码---------end-------

  return (
    <div>
      <p>You clicked {count} times</p>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        click me
      </button>
    </div>
  );
}
export default Example;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

解析:首先,我们生命了一个状态变量 count,将它的初始值设为 0,然后我们告诉 react,我们的这个组件有一个副作用。给 useEffecthook 传了一个匿名函数,这个匿名函数就是我们的副作用。在这里我们打印了一句话,当然你也可以手动的去修改一个 DOM 元素。当 React 要渲染组件时,它会记住用到的副作用,然后执行一次。等 Reat 更新了 State 状态时,它再一词执行定义的副作用函数。

# 3. 实现 componentWillUnmount 生命周期函数例子

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

  useEffect(() => {
    console.log(`useEffect=>You clicked ${count} times`);

    return () => {
      console.log("====================");
    };
  }, [count]);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        click me
      </button>

      <Router>
        <ul>
          <li>
            {" "}
            <Link to="/">首页</Link>{" "}
          </li>
          <li>
            <Link to="/list/">列表</Link>{" "}
          </li>
        </ul>
        <Route path="/" exact component={Index} />
        <Route path="/list/" component={List} />
      </Router>
    </div>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

补充:useEffect的第二个参数,它是一个数组,数组中可以写入很多状态对应的变量,意思是当状态值发生变化时,才进行解绑

# 4. 注意点

  • React 首次渲染和之后的每次渲染都会调用一遍 useEffect 函数,而之前我们要用两个生命周期函数分别表示首次渲染(componentDidMonut)和更新导致的重新渲染(componentDidUpdate)。

  • useEffect 中定义的函数的执行不会阻碍浏览器更新视图,也就是说这些函数时异步执行的,而 componentDidMonut 和 componentDidUpdate 中的代码都是同步执行的。

# 三.useContext

# 1.定义:

跨越组件层级直接传递变量,实现共享。作用就是对它所包含的组件树提供全局共享数据的一种技术。

需要注意的是useContextredux的作用是不同的,一个解决的是组件之间值传递的问题,一个是应用中统一管理状态的问题,但通过和useReducer的配合使用,可以实现类似Redux的作用。

# 2.例子

index.js

import React from "react";
import ReactDOM from "react-dom";
import Example from "./Example4";
ReactDOM.render(<Example />, document.getElementById("root"));
1
2
3
4

Example4.js


import React, { useState , createContext,useContext  } from 'react';
//===关键代码
const CountContext = createContext()

function Example4(){
    const [ count , setCount ] = useState(0);
    function Counter(){
        const count = useContext(CountContext)  //一句话就可以得到count
        return (<h2>{count}</h2>)
    }

    return (
        <div>
            <p>You clicked {count} times</p>
            <button onClick={()=>{setCount(count+1)}}>click me</button>
            {/*======关键代码
            要记得在<CountContext.Provider>的闭合标签中,
            */}
            <CountContext.Provider value={count}>
            <Counter />
            </CountContext.Provider>
        </div>
    )
}
export default Example4;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

解析:这段代码就相当于把 count 变量允许跨层级实现传递和使用了(也就是实现了上下文),当父组件的 count 变量发生变化时,子组件也会发生变化。

# 四.useReducer

# 1.定义:

可以增强我们的 Reducer,实现类似 Redux 的功能

# 2.例子 1:

import React, { useReducer } from "react";

function ReducerDemo() {
  const [count, dispatch] = useReducer((state, action) => {
    switch (action) {
      case "add":
        return state + 1;
      case "sub":
        return state - 1;
      default:
        return state;
    }
  }, 0);
  return (
    <div>
      <h2>现在的分数是{count}</h2>
      <button onClick={() => dispatch("add")}>Increment</button>
      <button onClick={() => dispatch("sub")}>Decrement</button>
    </div>
  );
}

export default ReducerDemo;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 3.例子 2:

Example6/showArea.js

import React, { useContext } from "react";
import { ColorContext } from "./color";

function ShowArea() {
  const { color } = useContext(ColorContext);
  return <div style={{ color: color }}>字体颜色为{color}</div>;
}

export default ShowArea;
1
2
3
4
5
6
7
8
9

Example6/Buttons.js

import React, { useContext } from "react";
import { ColorContext, UPDATE_COLOR } from "./color";

function Buttons() {
  const { dispatch } = useContext(ColorContext);
  return (
    <div>
      <button
        onClick={() => {
          dispatch({ type: UPDATE_COLOR, color: "red" });
        }}
      >
        红色
      </button>
      <button
        onClick={() => {
          dispatch({ type: UPDATE_COLOR, color: "yellow" });
        }}
      >
        黄色
      </button>
    </div>
  );
}

export default Buttons;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

color.js

import React, { createContext, useReducer } from "react";

export const ColorContext = createContext({});

export const UPDATE_COLOR = "UPDATE_COLOR";

const reducer = (state, action) => {
  switch (action.type) {
    case UPDATE_COLOR:
      return action.color;
    default:
      return state;
  }
};

export const Color = (props) => {
  const [color, dispatch] = useReducer(reducer, "blue");
  return (
    <ColorContext.Provider value={{ color, dispatch }}>
      {props.children}
    </ColorContext.Provider>
  );
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

Example6/Example6.js

import React, { useReducer } from "react";
import ShowArea from "./ShowArea";
import Buttons from "./Buttons";
import { Color } from "./color"; //引入Color组件

function Example6() {
  return (
    <div>
      <Color>
        <ShowArea />
        <Buttons />
      </Color>
    </div>
  );
}

export default Example6;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

src/index.js

import React from "react";
import ReactDOM from "react-dom";
import Example from "./Example6/Example6";

ReactDOM.render(<Example />, document.getElementById("root"));
1
2
3
4
5

# 四.useMeno

# 1.定义:

useMemo主要用来解决使用React hooks产生的无用渲染的性能问题。使用function的形式来声明组件,失去了shouldCompnentUpdate(在组件更新之前)这个生命周期,也就是说我们没有办法通过组件更新前条件来决定组件是否更新。而且在函数组件中,也不再区分mountupdate两个状态,这意味着函数组件的每一次调用都会执行内部的所有逻辑,就带来了非常大的性能损耗。useMemouseCallback都是解决上述性能问题的,这节课先学习 useMemo.

# 2.例子:

Example7.js

import React, { useState, useMemo } from "react";

function Example7() {
  const [a, setAtest] = useState("a test");
  const [b, setBtest] = useState("b test");
  return (
    <>
      <button
        onClick={() => {
          setAtest(new Date().getTime());
        }}
      >
        a
      </button>
      <button
        onClick={() => {
          setBtest(new Date().getTime() + ",b执行了");
        }}
      >
        b
      </button>
      <ChildComponent name={a}>{b}</ChildComponent>
    </>
  );
}
function ChildComponent({ name, children }) {
  function changeAtest(name) {
    console.log("atest执行了");
    return name + ",aaa执行了";
  }

  // const actionAtest = changeAtest(name) 点击b,a也会实行
  // useCallback(() => changeAtest(name), [name]);也可解决
  const actionAtest = useMemo(() => changeAtest(name), [name]); //只要使用useMemo,然后给她传递第二个参数,参数匹配成功,才会执行,点击b,a不会执行
  return (
    <>
      <div>{actionAtest}</div>
      <div>{children}</div>
    </>
  );
}
export default Example7;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# 五.useRef

# 1.定义:

用 useRef 获取 React JSX 中的 DOM 元素,获取后你就可以控制 DOM 的任何东西了。但是一般不建议这样来作,React 界面的变化可以通过状态来控制。

用 useRef 来保存变量,这个在工作中也很少能用到,我们有了 useContext 这样的保存其实意义不大

# 2.例子,useRef 获取 DOM 元素:

Example8.js

import React, { useRef } from "react";
function Example8() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    inputEl.current.value = "Hello ,world";
    console.log(inputEl); //输出获取到的DOM节点
  };
  return (
    <>
      {/*保存input的ref到inputEl */}
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>在input上展示文字</button>
    </>
  );
}
export default Example8;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 3.例子,useRef 保存普通变量:

import React, { useRef, useState, useEffect } from "react";
function Example8() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    inputEl.current.value = "Hello ,useRef";
    console.log(inputEl);
  };
  //-----------关键代码--------start
  const [text, setText] = useState("滚蛋");
  const textRef = useRef();

  useEffect(() => {
    textRef.current = text;
    console.log("textRef.current:", textRef.current);
  });
  //----------关键代码--------------end
  return (
    <>
      {/*保存input的ref到inputEl */}
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>在input上展示文字</button>
      <br />
      <br />
      <input
        value={text}
        onChange={(e) => {
          setText(e.target.value);
        }}
      />
    </>
  );
}

export default Example8;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 六.自定义 Hooks 函数获取窗口大小

# 1.定义:

其实自定义 Hooks 函数和用 Hooks 创建组件很相似,跟我们平时用 JavaScript 写函数几乎一模一样,可能就是多了些 React Hooks 的特性,自定义 Hooks 函数偏向于功能,而组件偏向于界面和业务逻辑。由于差别不大,所以使用起来也是很随意的。如果是小型项目是可以的,但是如果项目足够复杂,这会让项目结构不够清晰。所以学习自定义 Hooks 函数还是很有必要的

# 2.例子:

编写自定义函数

import React, { useState, useEffect, useCallback } from "react";
function useWinSize() {
  const [size, setSize] = useState({
    width: document.documentElement.clientWidth,
    height: document.documentElement.clientHeight,
  });

  const onResize = useCallback(() => {
    setSize({
      width: document.documentElement.clientWidth,
      height: document.documentElement.clientHeight,
    });
  }, []);
  useEffect(() => {
    window.addEventListener("resize", onResize);
    return () => {
      window.removeEventListener("resize", onResize);
    };
  }, []);

  return size;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

编写组件并使用自定义函数

function Example9() {
  const size = useWinSize();
  return (
    <div>
      页面Size:{size.width}x{size.height}
    </div>
  );
}

export default Example9;
1
2
3
4
5
6
7
8
9
10

# 七. useImperativeHandle

# 1.定义

  • useImperativeHandle可以通过使用 ref 时自定义暴露给父组件的实列值,并配合 forwardRef 一起使用。
  • 该钩子函数还有第三个参数,如果里面涉及到某个变化的值,只有当第三个参数发生改变时,父组件接收到的该值才会发生改变

# 2. 使用建议

在大多数情况下,应当避免使用 ref 这样的命令式代码。

# 3. 例子

// 子组件
import React, { useImperativeHandle, forwardRef, useState } from "react";
import { Button } from "antd";

// 注意: forwardRef必须传props
export default forwardRef((props, ref: any) => {
  const [preVal, setPreval] = useState("hello");

  // 透传给父组件的实例值
  useImperativeHandle(ref, () => ({ preVal }), [preVal]);

  return (
    <div>
      <Button onClick={() => setPreval("word")}>{preVal}</Button>
    </div>
  );
});
// 父组件
import React, { useRef } from "react";
import { Button, DatePicker } from "antd";

import Child from "./components/child";

export default () => {
  function onChange(date: any, dateString: string) {
    console.log(date, dateString);
  }
  // 获取子组件实例值
  const childRef = useRef();

  // 打印子组件实例值
  function getChildRefHandle() {
    console.log("childRef.current", childRef.current);
  }

  return (
    <div>
      <h1>users</h1>
      <Button type="primary">你好</Button>
      <DatePicker onChange={onChange} />
      <Child ref={childRef} />
      <Button onClick={getChildRefHandle}>获取子组件实例值</Button>
    </div>
  );
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45