一、React Hooks 介绍
1.1 React Hooks 是⽤来做什么的
对函数型组件进⾏增强, 让函数型组件可以存储状态, 可以拥有处理副作⽤的能⼒.
让开发者在不使⽤类组件的情况下, 实现相同的功能.
副作用是指:只要不是将数据转化为视图的代码。比如:获取Dom元素,为Dom元素添加事件,ajax请求等等
二、类组件的不足
1. 缺少逻辑复⽤机制
为了复⽤逻辑增加⽆实际渲染效果的组件,增加了组件层级 显示⼗分臃肿
增加了调试的难度以及运⾏效率的降低
2. 类组件经常会变得很复杂难以维护
将⼀组相⼲的业务逻辑拆分到了多个⽣命周期函数中
在⼀个⽣命周期函数内存在多个不相⼲的业务逻辑
3. 类成员⽅法不能保证this指向的正确性
三、使用useState让函数组件保存状态
Hooks 意为钩⼦, React Hooks 就是⼀堆钩⼦函数, React 通过这些钩⼦函数对函数型组件进⾏增强, 不同的钩⼦函数提供了不同的功能.
useState()
useEffects()
useReducer()
useRef()
useCallback()
useContext()
useMemo()
1、useState()
- 接收唯⼀的参数即状态初始值. 初始值可以是任意数据类型.
- 返回值为数组. 数组中存储状态值和更改状态值的⽅法. ⽅法名称约定以set开头, 后⾯加上状态名称.
- ⽅法可以被调⽤多次. ⽤以保存不同状态值.
- 参数可以是⼀个函数, 函数返回什么, 初始状态就是什么, 函数只会被调⽤⼀次, ⽤在初始值是动态值的情况
设置状态值⽅法的参数可以是⼀个值也可以是⼀个函数
设置状态值⽅法的⽅法本身是异步的
2、useReducer()
useReducer是另⼀种让函数组件保存状态的⽅式
3、useContext
没有用useContext之前传参
使用useContext优化
代码简洁很多
4、useEffect
让函数型组件拥有处理副作⽤的能⼒. 类似⽣命周期函数
可以把 useEffect 看做 componentDidMount, componentDidUpdate 和 componentWillUnmount 这三个函数的组合.
useEffect(() => {}) => componentDidMount, componentDidUpdate
useEffect(() => {}, []) => componentDidMount
useEffect(() => () => {}) => componentWillUnMount
监听页面滚动
组件销毁取消监听
卸载组件↓
只有指定数据发⽣变化时触发effect
5、useMemo()
useMemo 的⾏为类似Vue中的计算属性, 可以监测某个值的变化, 根据变化值计算新值.
useMemo 会缓存计算结果. 如果监测值没有发⽣变化, 即使组件重新渲染, 也不会重新计算. 此⾏为可以有助于避免在每个渲染上进⾏昂贵的计算
6、memo ⽅法
性能优化, 如果本组件中的数据没有发⽣变化, 阻⽌组件更新. 类似类组件中的 PureComponent 和 shouldComponentUpdate
7、useCallback()
Foo组件的状态即使不变化也被重新渲染。
解决↓
性能优化, 缓存函数, 使组件重新渲染时得到相同的函数实例.
8、useRef()
1、获取DOM元素对象
2、保存数据 (跨组件周期)
即使组件重新渲染, 保存的数据仍然还在. 保存的数据被更改不会触发组件重新渲染.
场景一、下面定时器不会被清除,因为数据变视图会更细,timerId都是重置为null
解决:
四、自定义hook
⾃定义 Hook 是标准的封装和共享逻辑的⽅式.
⾃定义 Hook 是⼀个函数, 其名称以 use 开头.
⾃定义 Hook 其实就是逻辑和内置 Hook 的组合
五、React 路由 Hooks
react-router-dom 路由提供的钩⼦函数
六、useState钩子函数的实现原理
let state = [];
let setters = [];
let stateIndex = 0;
function createSetter (index) {
return function (newState) {
state[index] = newState;
render ();
}
}
function useState (initialState) {
state[stateIndex] = state[stateIndex] ? state[stateIndex] : initialState;
setters.push(createSetter(stateIndex));
let value = state[stateIndex];
let setter = setters[stateIndex];
stateIndex++;
return [value, setter];
}
function render () {
stateIndex = 0;
effectIndex = 0;
ReactDOM.render(<App />, document.getElementById('root'));
}
function App() {
const [count, setCount] = useState(0)
const [name, setName] = useState('张三')
return <div>
{count}
{name}
</div>
}
七、useEffect钩子函数实现原理
// 上一次的依赖值
let prevDepsAry = [];
let effectIndex = 0;
function useEffect(callback, depsAry) {
// 判断callback是不是函数
if (Object.prototype.toString.call(callback) !== '[object Function]') throw new Error('useEffect函数的第一个参数必须是函数');
// 判断depsAry有没有被传递
if (typeof depsAry === 'undefined') {
// 没有传递
callback();
} else {
// 判断depsAry是不是数组
if (Object.prototype.toString.call(depsAry) !== '[object Array]') throw new Error('useEffect函数的第二个参数必须是数组');
// 获取上一次的状态值
let prevDeps = prevDepsAry[effectIndex];
// 将当前的依赖值和上一次的依赖值做对比 如果有变化 调用callback
let hasChanged = prevDeps ? depsAry.every((dep, index) => dep === prevDeps[index]) === false : true;
// 判断值是否有变化
if (hasChanged) {
callback();
}
// 同步依赖值
prevDepsAry[effectIndex] = depsAry;
effectIndex++;
}
}
八、useReducer钩子实现原理
function useReducer (reducer, initialState) {
const [state, setState] = useState(initialState);
function dispatch (action) {
const newState = reducer(state, action);
setState(newState);
}
return [state, dispatch];
}
function App() {
function reducer (state, action) {
switch (action.type) {
case 'increment':
return state + 1;
case 'decrement':
return state - 1;
default:
return state;
}
}
const [count, dispatch] = useReducer(reducer, 0);
return <div>
{count}
<button onClick={() => dispatch({type: 'increment'})}>+1</button>
<button onClick={() => dispatch({type: 'decrement'})}>-1</button>
</div>;
}