# 一.useState
# 1. 定义:
react 自带的一个 hook 函数,它的作用是用来声明状态变量。
# 2. 用法
- 声明
//数组结构
const [count, setCount] = useState(0);
// 不写成数组解构
// let _useState = userState(0)
// let count = _useState[0]
// let setCount = _useState[1]
2
3
4
5
6
- 读取
<p>You clicked {count} times</p>
- 使用
<button
onClick={() => {
setCount(count + 1);
}}
>
click me
</button>
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;
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>
);
}
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.定义:
跨越组件层级直接传递变量,实现共享。作用就是对它所包含的组件树提供全局共享数据的一种技术。
需要注意的是useContext和redux的作用是不同的,一个解决的是组件之间值传递的问题,一个是应用中统一管理状态的问题,但通过和useReducer的配合使用,可以实现类似Redux的作用。
# 2.例子
index.js
import React from "react";
import ReactDOM from "react-dom";
import Example from "./Example4";
ReactDOM.render(<Example />, document.getElementById("root"));
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;
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;
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;
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;
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>
);
};
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;
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"));
2
3
4
5
# 四.useMeno
# 1.定义:
useMemo主要用来解决使用React hooks产生的无用渲染的性能问题。使用function的形式来声明组件,失去了shouldCompnentUpdate(在组件更新之前)这个生命周期,也就是说我们没有办法通过组件更新前条件来决定组件是否更新。而且在函数组件中,也不再区分mount和update两个状态,这意味着函数组件的每一次调用都会执行内部的所有逻辑,就带来了非常大的性能损耗。useMemo和useCallback都是解决上述性能问题的,这节课先学习 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;
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;
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;
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;
}
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;
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>
);
};
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