React hooks + 函数组件

React hooks + 函数组件

一月 22, 2021

hooks + 函数组件

实质: 使用函数组件+hook 来实现 类组件的功能
官方期望用函数组件+hook 逐步代替类组件
为什么要代替类组件?

  • 降低 react 的入门门槛,增加开发者数量
  • 降低开发难度
  • 函数式编程

函数组件

  • 函数组件就是一个函数
  • 函数组件由两个参数\
    • props 就是绑定在 App 组件身上的 props
    • ref ref 绑定,用于获取内容
  • 函数组件特点
    • this 为 undefined
    • 如果只使用函数组件,那么 react 的很多特性都不可以使用了,比如: state \ setState \ forceUpdate \contextType \ 生命周期 。。。
1
2
3
4
5
interface P {}
const App = (props: P, ref: any) => {
return <div> App </div>;
};
export default App;

Hook 钩子函数

  • react 16.8 版本之后才提供了一个新的特性:Hook
  • Hook + 函数组件才能实现 react 的特性

Hook

官方提供了 10 个内置 Hook

useState(initValue) // initValue 初始值

用于函数组件定义 state,和修改 state 的方法

  • 定义格式: const [stateName,setStateName] = useState<数据类型>( 初始值 )

  • 比如: const [name,setName] = useState(‘lakers’)

  • 注意点

    • 基础数据类型: 直接赋值

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      import React, { useState } from "react";

      export default function App() {
      const [name, setName] = useState<string>("yyc");
      return (
      <div>
      <button
      onClick={() => {
      setName("yyc2");
      }}
      >
      修改名字
      </button>
      <p>{name}</p>
      </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
      import React, { useState } from "react";

      interface People {
      name: string;
      }

      export default function App() {
      const [people, setPeople] = useState<People>({
      name: "fox",
      });

      const [arr, setArr] = useState<unknown[]>([1, 2, 3, 4]);
      const changeName = () => {
      // 方法一
      // people.name = "dog";
      // setPeople({ ...people });

      // 方法二
      setPeople({
      name: "dog",
      });

      // 结论: 引用数据类型不允许直接修改 setXxx的值必须是一个新值
      };

      const add = () => {
      arr.push(arr.length + 1);
      setArr([...arr]);
      };
      return (
      <div>
      <button onClick={changeName}>修改名字</button>
      <p>{people.name}</p>
      <button onClick={add}>增加</button>
      <ul>
      {arr.map((item, index) => (
      <li key={index}> {item} </li>
      ))}
      </ul>
      </div>
      );
      }

useEffect

useEffect 可以让函数组件实现类组件部分生命周期的功能
可以实现以下几个生命周期钩子函数的功能

  1. componentDidMount
  2. componentDidUpdate [ 属性或者状态的监听 ]
  3. componentWillUnmount
  4. useEffect(回调函数,依赖项)
  1. 有返回值,而且返回值是一个函数,—–> 相当于 componentWillUnmount
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// App.tsx 用于创建销毁子组件
import React from "react";
import Hello from "./Hello";

export default function App() {
const [flag, setFlag] = React.useState<boolean>(true);
return (
<div>
<button
onClick={() => {
setFlag(false);
}}
>
{" "}
销毁{" "}
</button>
{flag && <Hello />}
</div>
);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Hello.tsx 子组件
import { time } from "console";
import React, { useEffect } from "react";

export default function Hello() {
let timer: any = null;
useEffect(() => {
console.log("创建了");
// 在挂载阶段创建计时器
timer = setInterval(() => {
console.log("1111");
}, 1000);
}, []);
useEffect(() => {
return () => {
console.log("销毁了");
// 需要手动销毁计时器(第三方实例,window或者document上的事件)
clearInterval(timer);
};
});
return <div>hello</div>;
}
  1. 依赖项就是一个数组
  2. 可以是空数组 —–> 相当于 componentDidMount
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import React, { useEffect } from "react";

export default function App() {
useEffect(() => {
// 创建时就执行
console.log("mount");
// 真实DOM操作
const pTxt: any = document.querySelector("p");
pTxt.style.background = "pink";
// 数据请求
// 。。。省略
}, []);
return (
<div>
<p>123</p>
</div>
);
}
  1. 数组中也可以放 state 或者 props ——> componentDidUpdate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import React, { useEffect, useState } from "react";

export default function App() {
const [count, setCount] = useState<number>(0);
useEffect(() => {
console.log("执行");
}, [count]);
return (
<div>
<button
onClick={() => {
setCount((pre) => pre + 1);
}}
>
按钮
</button>
<p>{count}</p>
</div>
);
}

useContext

  1. 新建 context 文件夹/index.ts 文件
1
2
3
4
5
6
//todo 创建上下文对象
import React, { createContext } from "react";

const ctx = createContext(0);

export default ctx;
  1. 用 ctx.Provider 包裹
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React, { useState } from "react";
import Father from "./components/Father";
import ctx from "./context";

export default function App() {
const [money] = useState<number>(200000);
return (
<div>
<h3> useContext -- 跨组件通信 </h3>
<ctx.Provider value={money}>
<Father />
</ctx.Provider>
</div>
);
}
  1. 子组件用 useContext 来接收
1
2
3
4
5
6
7
import React, { useContext } from "react";
import ctx from "../context";

export default function GrandSon() {
const money = useContext(ctx);
return <div>{money}</div>;
}

useRef + useImperativeHandle

  1. 可以绑定元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React, { useRef } from "react";

export default function App() {
const pRef: any = useRef(null);
const setBgc = () => {
// console.log(pRef);
pRef.current.style.background = "pink";
};
return (
<div>
<button onClick={setBgc}>按钮</button>
<p ref={pRef}>123</p>
</div>
);
}
  1. 可以绑定类组件(计数案例)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// App.tsx
import React, { useRef } from "react";
import Hello from "./Hello";

export default function App() {
const helloRef: any = useRef(null);
const addVal = () => {
helloRef.current.add();
};
return (
<div>
<button onClick={addVal}>+</button>
<Hello ref={helloRef} />
</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
// Hello.tsx
import React, { Component } from "react";

interface P {}

interface S {
count: number;
}
export default class Hello extends Component<P, S> {
constructor(props: P) {
super(props);
this.state = {
count: 0,
};
}
add = () => {
this.setState({
count: this.state.count + 1,
});
};
render() {
const { count } = this.state;
return (
<div>
<p> {count} </p>
</div>
);
}
}
  1. 不可以绑定函数组件(解决:使用 React.forwardRef + useImperativeHandle)
  2. useImperativeHandle(ref,()=>{})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// App.tsx
import React, { useRef } from "react";
import Hello from "./Hello";

export default function App() {
const helloRef: any = useRef(null);
const addVal = () => {
helloRef.current.add();
};
return (
<div>
<button onClick={addVal}>+</button>
<Hello ref={helloRef} />
</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
// Hello.tsx
import React, { useState, useImperativeHandle } from "react";

function Hello(props: any, ref: any) {
const [count, setCount] = useState<number>(0);
const add = () => {
setCount((pre) => {
return pre + 1;
});
};
useImperativeHandle(
ref, // 这里的ref是函数组件的参数
() => {
// handler; 用于导出 就是ref.current的属性
return {
add,
};
}
);
return (
<div>
<p>{count}</p>
</div>
);
}

export default React.forwardRef(Hello);

useReducer

  • 用于定义状态,修改状态,和 useState 相似
1
2
3
const [state, dispatch] = useReducer(reducer, initialState);
// reducer(state,action) 用于编辑方法和设置state
// initialState 用于定义数据state

案例:

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
import React, { useReducer } from "react";

const initialState = {
count: 0,
};

const reducer = (state: any, action: any) => {
switch (action.type) {
case "in":
return { count: state.count + 1 };
case "de":
return { count: state.count - 1 };
default:
throw new Error();
}
};

export default function App() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<button
onClick={() => {
dispatch({
type: "in",
});
}}
>
+
</button>
<hr />
<button
onClick={() => {
dispatch({
type: "de",
});
}}
>
-
</button>
<p>{state.count}</p>
</div>
);
}

hooks 中用于渲染优化的(useMemo/useCallback)

  1. useMemo(返回的是一个记忆值,直接用)
  2. 参数(callback,array) 第一个为函数方法,第二个为数组 填依赖项
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
import React, { useState, useMemo } from "react";

export default function App() {
const [count, setCount] = useState<number>(0);
const [list] = useState<number[]>([1, 2, 3, 4]);
const add = () => {
setCount((pre) => pre + 1);
};
const renderList = () => {
console.log("chufale");
return list.map((item, index) => <li key={index}> {item} </li>);
};
// useMemo(()=>{},[])
// 第一个参数 方法(回调函数)
// 第二个参数 []数组 里面填依赖项
const memoList = useMemo(renderList, [list]);
return (
<div>
<button onClick={add}>+</button>
<p>{count}</p>
{/* <ul>{renderList()}</ul> */}
这里出现的问题 点击增加会重新渲染list列表
<br />
解决办法:useMemo
{memoList}
</div>
);
}
  1. useCallback(callback,array) 用法和 useMemo 一样
    1. useCallback 返回值是一个记忆函数
    1. 这个记忆函数必须传递给子组件用
    1. 这个子组件必须是 React.memo() 的参数
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
// App.tsx 父组件
import React, { useState, useCallback } from "react";
import Hello from "./Hello";

export default function App() {
const [count, setCount] = useState<number>(0);
const [list] = useState<number[]>([1, 2, 3, 4]);
const add = () => {
setCount((pre) => pre + 1);
};
const renderList = () => {
console.log("chufale");
return list.map((item, index) => <li key={index}> {item} </li>);
};

const callbackList = useCallback(renderList, [list]);
return (
<div>
<button onClick={add}>+</button>
<p>{count}</p>
{/* <ul>{renderList()}</ul> */}
这里出现的问题 点击增加会重新渲染list列表
<br />
解决办法:useCallback
<Hello callbackList={callbackList} />
</div>
);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Hello.tsx 子组件
import React, { memo, ReactNode } from "react";

interface P {
callbackList: () => ReactNode;
}
function Hello(props: P) {
return (
<div>
<ul>{props.callbackList()}</ul>
</div>
);
}

export default memo(Hello);

useLayoutEffect 用法和 useEffect 一致

useDebugValue

  • 给自定义 hook 在谷歌提示工具中起名的

自定义 HOOK 封装

  1. 封装一个函数,只不过这个函数有些特别【useState,useEffect】
  2. 自定义 Hook 名称
    • useXxxx
  3. 其他 hooks 封装: