1、hooks是用来做什么的?
对函数组件进行增强,让函数型组件可以存储状态,可以拥有处理副作用的能力。 副作用:除了直接展示dom视图外的其他函数都是
2、类组件的不足
- 缺失逻辑复用机制,为了复用逻辑增加无实际渲染效果的组件,增加了组件层级 显示十分臃肿,增加了调试的难度以及运行效率的 降低
- 类组件经常会变得很复杂难以维护,将一组相干的业务逻辑拆分到了多个生命周期函数中,在一个生命周期函数内存在多个不相干的业务逻辑
- 类成员方法不能保证this指向的正确性
3、useState
参数可以是一个函数,函数返回什么,初始状态就是什么,函数只调用一次,用在初始值是动态值的情况
const[count, setCount]=useState(()=>(props.count || 0))
设置状态值方法的参数可以是一个值也可以是一个函数,设置方法本身是异步的
4、useReducer
一种让函数组件保存状态的方式
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)
5、useContext
在跨组件层级获取数据时简化获取数据的代码
import React, { createContext, useContext } from 'react'
const countContext = createContext()
function App() {
return <countContext.Provider value={100}>
<Foo />
</countContext.Provider>
}
function Foo() {
const value = useContext(contextValue)
return <div>{value}</div>
6、useEffect
- 让函数型组件拥有处理副作用的能力,类似生命周期函数
- 结合异步函数
- useEffect中的参数函数不能是异步函数,因为useEffect函数要返回清理资源的函数,如果是异步函数就变成 了promise
useEffect(() => {
(async () => {
await axios.get();
})();
}, []);
7、useMemo
- 类似于Vue中的计算属性,可以监测某个值的变化,根据变化值计算新值
- useMemo会缓存计算结果,如果监测没有变化,即使组件重新渲染,也不会重新计算,此行为有助于避免在每 个渲染上进行昂贵的计算
let result = useMemo(() => {
return count; // 操作
}, [count]);
8、memo
性能优化,如果本组件中的数据没有发生变化,阻止组件更新,类似类组件的PureComponent和 shouldComponentUpdate
const Counter = () => {
return <div></div>;
};
export default memo(Counter);
9、useCallback
性能优化,缓存函数,是组件重新渲染时得到相同的函数实例
const resetCount = useCallback(() => setCount(count + 1), [setCount]);
10、useRef
获取dom元素
function App() {
const box = useRef(); // { current: div }
return (
<div ref={box}>
<Button onClick={() => console.log(box)}></Button>
</div>
);
}
- 保存数据(跨组件周期)
- 即使组件重新渲染,保存的数据仍然存在,保存的数据被更改不会触发组件重新渲染
function App() {
const [count, setCount] = useState(0);
let timerId = useRef();
useEffect(() => {
timerId.current = setInterval(() => {
setCount((count) => count + 1);
}, 1000);
}, []);
const stopCount = () => {
clearInterval(timerId.current);
};
return (
<div>
{count}
<Button onClick={stopCount}>停止</Button>
</div>
);
}
11、自定义hook
- 标准的封装和共享逻辑的方式
- 是一个函数,以use开头
- 逻辑和内置hook的组合
12、路由hooks
import { useHistory, useLocation, useRouterMatch, useParams } from 'react-router-dom';
13、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));
stateIndex++;
let value = state[stateIndex];
let setter = setters[stateIndex];
return [value, setter];
}
为什么useState不能放在条件语句?
- 因为react通过单链表来管理hooks
update阶段,每次调用useState,链表就会执行next向后移动一步,如果将useState放在条件语句中,假设条件判断不成立,没有执行里面的useState方法,会导致接下来所有的useState的取值出现偏移,从而导致异常发生。
参考链接: juejin.cn/post/684490…
14、useEffect实现原理
let preDepsAry = [];
let effectIndex = 0;
function useEffect(callback, depsAry) {
if (Object.prototype.toString.call(callback) !== '[object Function]')
throw new Error('useEffect函数的第一个参数必须是函数');
if (typeof depsAry === 'undefined') {
callback();
} else {
if (Object.prototype.toString.call(depsAry) !== '[object Array]')
throw new Error('depsAry不是一个数组');
//判断值是否有变化
let prevDeps = preDepsAry[effectIndex];
let hasChanged = prevDeps
? depsAry.every((dep, index) => dep === preDepsAry[index]) === false
: true;
if (hasChanged) {
callback();
}
//同步依赖值
preDepsAry[effectIndex] = depsAry;
effectIndex++;
}
}
15、useReducer实现原理
function useReducer(reducer, initialState) {
const [state, setState] = useState(initialState);
function dispatch(action) {
const newState = reducer(state, action);
setState(newState);
}
return [state, dispatch];
}
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);