为了解决一些component问题:
比如说:
有了Hooks:
Hooks优点:
Hooks缺点(Hoosk有哪些坑):
原因:push,pop,splice是直接修改原数组,react会认为state并没有发生变化,无法更新)
问题
:每次 render 都有一份新的状态,数据卡在闭包里,捕获了每次 render 后的 state,也就导致了输出原来的 state
解决
:可以通过 useRef 来保存 state。前文讲过 ref 在组件中只存在一份,无论何时使用它的引用都不会产生变化,因此可以来解决闭包引发的问题。
官网文档
useState()
状态钩子。为函数组建提供内部状态
useContext()
共享钩子。该钩子的作用是,在组件之间共享状态。 可以解决react逐层通过Porps传递数据,它接受React.createContext()的返回结果作为参数,使用useContext将不再需要Provider 和 Consumer
useReducer()
状态钩子。Action 钩子。useReducer() 提供了状态管理,其基本原理是通过用户在页面中发起action, 从而通过reducer方法来改变state, 从而实现页面和状态的通信。使用很像redux
useEffect()
副作用钩子。它接收两个参数, 第一个是进行的异步操作, 第二个是数组,用来给出Effect的依赖项
useRef()
获取组件的实例;渲染周期之间共享数据的存储(state不能存储跨渲染周期的数据,因为state的保存会触发组件重渲染),useRef传入一个参数initValue,并创建一个对象{ current: initValue }给函数组件使用,在整个生命周期中该对象保持不变
useMemo和useCallback
可缓存函数的引用或值,useMemo缓存数据,useCallback缓存函数,两者是Hooks的常见优化策略,useCallback(fn,deps)相当于useMemo(()=>fn,deps)
,经常用在下面两种场景:
1、要保持引用相等;对于组件内部用到的 object、array、函数等,
2、用在了其他 Hook 的依赖数组中,或者作为 props 传递给了下游组件,应该使用 useMemo/useCallback)
浅比较
用链表数据结构来做全局状态保持;判断依赖项决定是否要更新状态等等
useState 和 useReducer 都是关于状态值的提取和更新, useState 是 useReducer 的一个简化版,
1、两者的状态值都被挂载在组件实例对象 FiberNode 的 memoizedState 属性中
2、两者保存状态值的数据结构完全不同;类组件是直接把 state 属性中挂载的这个开发者自定义的对象给保存到 memoizedState 属性中;而 React Hooks 是用链表来保存状态的, memoizedState 属性保存的实际上是这个链表的头指针。
//链表的节点--Hook对象 react-reconciler/src/ReactFiberHooks.js
export type Hook={
memoizedState:any,//最新的状态值
baseState:any,//初始状态值,如`useState(0)`,则初始值为0
baseUpdate:Update|null,
queue:UpdateQueue | null,//临时保存对状态值的操作,更准确来说是一个链表数据结构中的一个指针
next:Hook | null,//指向下一个链表节点
}
Hooks模拟constructor
constructor(){
super()
this.state={count:0}
}
//Hooks模拟constructor
const [count setCount]=useState(0)
Hooks模拟componentDidMount
componentDidMount(){
console.log('I am mounted')
}
//Hooks模拟componentDidMount
useEffect(()=>console.log('mounted'),[])
//useEffect拥有两个参数,第一个参数作为回调函数会在浏览器布局和绘制完成后调用,因此它不会阻碍浏览器的渲染进程,第二个参数是一个数组,也是依赖项
//1、当依赖列表存在并有值,如果列表中的任何值发生更改,则每次渲染后都会触发回调
//2、当它不存在时,每次渲染后都会触发回调
//3、当它是一个空列表时,回调只会被触发一次,类似于componentDidMount
模拟shouldComponentUpdate
shouldComponentUpdate(nextProps,nextState){
console.log('shouldComponentUpdate')
return true //更新组件 反之不更新
}
// React.memo包裹一个组件来对它的props进行浅比较,但这不是一个hooks,因为它的写法和hooks不同,其实React.memo等效于PureComponent,但它只比较props
// 模拟shouldComponentUpdate
const MyComponent=React.memo(
_MyComponent,
(prevProps,nextProps)=>nextProps.count!==preProps.count
)
Hooks模拟componentDidUpdate
componentDidMount() {console.log('mounted or updated');}
componentDidUpate(){console.log('mounted or updated')}
//Hooks模拟componentDidUpdate
useEffect(()=>console.log('mounted or updated'))
//这里的回调函数会在每次渲染后调用,因此不仅可以访问componentDidUpdate,还可以访问componentDidMount,如果只想模拟componentDidUpdate,我们可以这样来实现
const mounted=useRef()
useEffect(()=>{
if(!mounted.current){mounted.current=true}else{console.log('I am didUpdate')}
})
//useRef在组件中创建“实例变量”,它作为一个标志来指示组件是否处于挂载或更新阶段。当组件更新完成后在会执行else里面的内容,以此来单独模拟componentDidUpdate
Hooks模拟componentWillUnmount
componentWillUnmount(){
console.log('will unmount')
}
//hooks
useEffect(()=>{
//此处并不同于willUnMount porps发生变化即更新,也会执行结束监听
//准确的说:返回的函数会在下一次effect执行之前,被执行
return ()=>{console.log('will unmount')}
},[])
//当在useEffect的回调函数中返回一个函数时,这个函数会在组件卸载前被调用。我们可以在这里清除定时器或事件监听器。
1、默认的useEffect(不带[])中return的清理函数,它和componentWillUnmount有本质区别的,默认情况下return,在每次useEffect执行前都会执行,并不是只有组件卸载的时候执行。
2、useEffect在副作用结束之后,会延迟一段时间执行,并非同步执行
,和compontDidMount有本质区别。遇到dom操作,最好使用useLayoutEffect。
hooks 模拟的生命周期与class中的生命周期不尽相同,我们在使用时,还是需要思考业务场景下那种方式最适合。
hoc和render prop都是一种开发模式,将复用逻辑提升到父组件,容易嵌套过多,过度包装
hooks是react的api模式,将复用逻辑取到组件顶层,而不是强行提升到父组件中。这样就能够避免 HOC 和 Render Props 带来的「嵌套地域」
对于class com来说:
- 首先state是不可变(Immutable),setState后生成一个全新的state引用
- 但class com通过this.state方式读取state,所以每次代码执行都会拿到最新的state引用
对于function com来说:
- useState 产生的数据也是不可变 Immutable 的,通过数组第二个参数 Set 一个新值后,原来的值在下次渲染时会形成一个新的引用。
- 但它state没有通过this.的方式读取,每次执行都读取当时渲染闭包环境的数据,虽然最新的值跟着最新的渲染变了,但旧的渲染里,状态依然是旧值
1、原理:function组件能捕获渲染的值(captaure the rendered values),读取渲染闭包内的数据,而class组件在react通过this.的方式读取,this是可变的,所以总能获取最新的props
2、保存状态:Class把state属性挂载的对象保存到memoizedState属性中,而Function是用链表来保存状态的,memoizedState属性保存是链表的头指针
1、useEffect是render结束后,callback函数执行,但是不会阻断浏览器的渲染,算是某种异步的方式
吧。但是class的componentDidMount 和componentDidUpdate是同步的,在render结束后就运行,useEffect在大部分场景下都比class的方式性能更好.
2、useLayoutEffect是用在处理DOM的时候,当你的useEffect里面的操作需要处理DOM,并且会改变页面的样式,就需要用这个,否则可能会出现出现闪屏问题, useLayoutEffect里面的callback函数会在DOM更新完成后立即执行
,但是会在浏览器进行任何绘制之前运行完成,阻塞了浏览器的绘制
//setState
this.setState(
{count:this.state.count+1},
()=>{
console.log(this.state.count) //通过回调函数监听到最新的值
})
//useState
const[count,setCount]=useState(0)
setCount(1)
useEffect(()=>{
console.log(count) //通过useEffect监听最新的值
},
[count])
1、setState是通过回调函数来获取更新的state,useState是通过useEffect() 来获取最新的 state
2、二者第一个参数都可以传入函数
3、setState()可以在第2个参数传入回调,useState()没有第2个参数
4、setState()自动具备浅合并功能,useState()更新引用需要手动浅合并
setState(updater,[,callback])
updater:object/function - 用于更新数据
callback:function - 用于获取更新后最新的state值
useState(initState)
const [state ,setState] = useState(initState)
state:状态
setState(updater): 修改状态的方法
updater:object/function - 用于更新数据
initState : 状态的初始值
useState 通过数组第二个参数 Set 一个新值后,新值会形成一个新的引用,捕获当时渲染闭包里的数据 State
setState 是通过 this.state 的读取 state,每次代码执行都会拿到最新的 state 引用
//方法一 模拟setState传入updater和callback
const [n1,setN1]=useState(0)
const [n1,setN2]=useState(0)
setN1((num)=>{
setN2(num+1)
//返回n1修改后的值
return num+1
})
//方法二 自定义hooks,配合引入useRef
export const useXState =(initState)=>{
const [state,setState]=useState(initState)
let isUpdate=useRef()
const setXState=(state,cb)=>{
setState(prev=>{
isUpdate.current=cb
return typeof state==='function'?state(prev):state
})
}
}
useEffect(()=>{
if(isUpdate.current){
isUpdate.current()
}
//useEffect不可以每次渲染组件都执行,因此在每次渲染之后都需要判断其是否值得执行
})
return [state,setXState]
//useRef的特性来作为标识区分是挂载还是更新,当执行setXstate时,会传入和setState一模一样的参数,并且将回调赋值给useRef的current属性,这样在更新完成时,我们手动调用current即可实现更新后的回调这一功能
import { useState, useEffect } from 'react';
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
});
return isOnline;
}
1、属性代理。高阶组件通过包裹的React组件来操作props,更改 props,可以对传递的包裹组件的WrappedComponent的props进行控制
2、通过 refs 获取组件实例
https://blog.csdn.net/kellywong/article/details/106430977