Hooks
函数组件 与 类组件
- 侧重点:
- 类组件: 面相对象的编程思想 (对象的创建 => 继承和复用)
- 函数组件: 面向函数的编程思想 (函数执行)
react组件是干嘛的?
- render(data) => UI => 浏览器
- 渲染UI
类组件
基于面相对象思想构建的组件体系,主要是更好的组织代码
- 优点:
- 有内部状态
- 完整的生命周期(后续去看看)
- 单独的render函数
- 缺点:
- 限制了组件方法逻辑的抽离
- 复杂的this的指向与生命周期的使用
- 组件的状态很难复用(因为状态是服务于当前组件的)
class Test {
state = {}
render() {
return <>render</>
}
}
函数组件
基于函数编程构建组件的方式,主要是为了增加组件代码的自由度
- 优点:
- 从组件render的特性出发,一次执行一次返回视图
- 希望开发者能把功能能更好的抽离出去
- 更好逻辑复用,拥有更高的自由度
- 缺点:
- 组件无状态
- 生命周期不完整
- 每次更新都需要执行函数
React Hooks
16.8推出的新功能,都是服务于函数组件的
- useState: 创建状态
- useReducer: 创建数据与复杂的数据操作
- useEffect: 处理程序中的副作用
- useLayoutEffect:
- useContext: 创建组件体系的上下文对象
- useMemo: 创建可以计算的数据(computed)
- useCallback: 缓存方法
- useRef: 获取节点实例
- useImprativeHandle
- useSyncExternalStore
- useTransition
- ...
特点:
- 只能在函数组件中使用
- hook都已已
use
开头的函数(包括自定义hook) - hook应该在函数内部最顶层使用(顺序定义、顺序调用)
- hook不能写在判读语句中
demo1
import { useState } from "react";
function State() {
/**
* [状态, 设置状态的方法]
* setCount(value)
* setCount((state) => newState)
* 注意:
* 1. count: 是只读的,必须通过 setCount去更新值
* 2. 设置值的时候,必须是新值或者新的引用
* 3. react对比值是浅对比
*/
const [count, setCount] = useState(0);
const [info, setInfo] = useState({
name: "xiaobai",
age: 18
})
const none = () => {
info.name = "不起效果";
setInfo(info);
}
return (
<div>
<h4>
{info.name}-{count}
</h4>
<button onClick={() => setCount(count + 1)}>增</button>
<button onClick={() => setCount((count) => count - 1)}>减</button>
<button onClick={() => setInfo({ ...info, name: "大白" })}>
changeName
</button>
<button onClick={none}>不起效果</button>
</div>
);
}
export default State;
demo2 todo
- **useState源码模拟实现
#### 源码模拟
```js
import _ from 'lodash';
const states = [];
const stateSetters = [];
let stateIndex = 0;
function useState(data) {
state[stateIndex] = states[stateIndex] || _.cloneDeep(data);
if(typeof stateSetters[stateIndex] !== 'function') {
stateSetters[stateIndex] = ((stateIndex) => {
return (value) => {
if(state[stateIndex] !== value) {
state[stateIndex] = value;
render();//模拟的方法
}
}
})(stateIndex);
}
const state = states[stateIndex];
const setState = stateSetters[stateIndex];
stateIndex++;
return [state, setState];
}
#### useReducer
```jsx
// 下面的操作是集成非常低的
setNum(num => num - 1);
setNum(num => num + 1);
- 如果一个状态的操作方式有很多种的情况下,useReducer是最佳的选择
demo1
import { useReducer } from "react";
function Reducer() {
/**
* dipatch({type: 'plus'}) => setNum(num => num + 1);
* dipatch({type: 'minus'}) => setNum(num => num - 1);
* reducer: dispath需要的方法集合
* 0: 叫做初始值
*/
const [num, dispatch] = useReducer(reducer, 0);
/**
* @param {*} state num
* @param {*} action
*/
function reducer(state, action = {}) {
let step = Number(action.payload) || 1;
switch(action.type) {
case "PLUS":
return state + step;
case "MINUS":
return state - step;
default:
return state;
}
}
return (
<div>
<h4>{num}</h4>
<button onClick={() => dispatch({ type: "PLUS" })}>增</button>
<button onClick={() => dispatch({ type: "MINUS" })}>减</button>
<button onClick={() => dispatch({ type: "PLUS", payload: 5 })}>增5</button>
<button onClick={() => dispatch({ type: "MINUS", payload: 4 })}>减4</button>
</div>
);
}
export default Reducer;
- useReducer流程示意图如下:
useEffect
- 处理副作用的回调: 与视图渲染无关的一些操作
- 依赖项:
- 依赖项中的依赖改变后,回调会重新执行
useEffect(() => {}, [依赖项])
demo1
import { useEffect, useState } from "react"
function Effect() {
const [num, setNum] = useState(0);
const [age, setAge] = useState(14);
useEffect(() => {
// 一般用来请求数据,初始化组件的设置
console.log("没有依赖,只在组件挂载完成后执行一次")
}, []);
useEffect(() => {
// 不推荐使用,极为不安全的
console.log("没有依赖项参数,组件重新渲染完成后都会执行")
});
useEffect(() => {
console.log("有依赖项age,组件重新渲染完成后都会执行")
}, [age]);
console.log("render");
return (
<div>
<h4>num:{num}</h4>
<h4>age:{age}</h4>
<button onClick={() => setNum(num + 1)}>+</button>
<button onClick={() => setAge(age + 1)}>长大</button>
</div>
);
}
export default Effect
第一次执行函数组件
点击
+
:第二次执行函数组件
点击
长大
: 第三次执行函数组件
- 注意:
- 在render完成后执行
- 任意一个依赖项中依赖发生变化,有清除副作用函数执行上一个清理函数,再回调重新执行
- 填入依赖的标准是回调的内部需要依赖项参与程序,一般是props 或者 state
- 不用的东西不要去填
- 回调不能写异步函数,因为回调内部会返回一个清除副作用函数
- 组件卸载的时候,不管有没有依赖项都会执行掉
清理函数
当你的项目足够大并且复杂时候尽量把数据封装redux中