render方法
function render(){
ReactDOM.render(
<Counter />,
document.getElementById('root')
);
}
render()
useState
- 全局数组来保存这些state
- 闭包索引来指向每一次对应的useState
let lastState = [] //存储每一次useState的值
let stateIndex = 0
function useState(initState) {
//进行保存
lastState[stateIndex] = lastState[stateIndex] || initState;
//索引指向当前state存储的位置
const currentIndex = stateIndex
function setState(newState) {
//通过闭包保存每一个state的索引
lastState[currentIndex ] = newState
//重新渲染
render()
}
return [lastState[stateIndex++],setState]
}
useCallback
当传递子组件是一个方法时候,看似方法没改变,其实旧的和新的方法是不一样的,会重新创建函数,每一次点击按钮子组件都会重新渲染。
useCallback则会使用全局变量保存函数地址,使得不会重新渲染组件
- 用全局变量把callback和dependencies依赖项保存下来。
- 首先useCallback会判断我们是否传入了依赖项,如果没有传的话,说明要每一次执行useCallback都返回最新的callback
//存储的callback
let lastCallback
//依赖项
let lastCallbackDependencies
function useCallback(callback,dependencies){
//如果传入了依赖项
if(lastCallbackDependencies){
//判断依赖项是否改变
let changed = !dependencies.every((item,index)=>{
return item === lastCallbackDependencies[index]
})
if(changed){
lastCallback = callback
lastCallbackDependencies = dependencies
}
}else{
// 没有传入依赖项,则每次都要改变
lastCallback = callback
lastCallbackDependencies = dependencies
}
return lastCallback
}
useMemo
- seMemo用于缓存值
let lastMemo
let lastMemoDependencies
function useMemo(callback,dependencies){
//判断是否传入依赖项
if(lastMemoDependencies){
let changed = !dependencies.every((item,index)=>{
return item === lastMemoDependencies[index]
})
//依赖项改变重新计算赋值
if(changed){
lastMemo = callback()
lastMemoDependencies = dependencies
}
}else{
// 没有传入依赖项,每次都重新计算赋值
lastMemo = callback()
lastMemoDependencies = dependencies
}
return lastMemo
}
useReducer
对于复杂的state结构,推荐使用useReducer,useReducer可以看作是useState的复杂增强版
基本使用:
// 官方 useReducer Demo
// 第一个参数:应用的初始化
const initialState = {
count: 0};
// 第二个参数:state的reducer处理函数
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {
count: state.count + 1};
case 'decrement':
return {
count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
// 返回值:最新的state和dispatch函数
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
// useReducer会根据dispatch的action,返回最终的state,并触发rerender
Count: {
state.count}
// dispatch 用来接收一个 action参数「reducer中的action」,用来触发reducer函数,更新最新的状态
<button onClick={
() => dispatch({
type: 'increment'})}>+</button>
<button onClick={
() => dispatch({
type: 'decrement'})}>-</button>
</>
);
}
实现原理:
//全局保存每一次dipatch后state的值
let lastState
function useReducer(reducer,initialState){
lastState = lastState || initialState
function dispatch(action){
//调用传入的reducer方法,和action的type类型即可
lastState = reducer(lastState,action)
//重新渲染
render()
}
//返回最新值和type方法
return [lastState,dispatch]
}
useEffect
- 作为第一个参数的函数是在浏览器渲染结束后执行的即宏任务-》渲染-》宏任务
- 类似生命周期效果的实现,实际每次都是setState后重新render过后,重新执行组件,重新执行useEffect
//全局保存依赖项
let lastEffectDependencies
function useEffect(callback,dependencies){
//如果传入了依赖项
if(lastEffectDependencies){
let changed = !dependencies.every((item,index)=>{
return item === lastEffectDependencies[index]
})
//如果依赖项改变,重新执行并赋值
if(changed){
setTimeout(callback())
lastEffectDependencies = dependencies
}
}else{
//未传入依赖项,每次都执行
setTimeout(callback())
lastEffectDependencies = dependencies
}
}
useLayoutEffect
- 原理跟useEffect一样,只是调用时机不同
- useEffect的调用时机是浏览器渲染结束后执行的,而useLayoutEffect是在DOM构建完成,浏览器渲染前执行的即宏任务(微任务,这么写原因是微任务是宏任务的一部分)->渲染->宏任务(微任务)
let lastEffectDependencies
function useLayouyEffect(callback,dependencies){
if(lastEffectDependencies){
let changed = !dependencies.every((item,index)=>{
return item === lastEffectDependencies[index]
})
if(changed){
Promise.resolve().then(callback())
lastEffectDependencies = dependencies
}
}else{
Promise.resolve().then(callback())
lastEffectDependencies = dependencies
}
}