本文已参与「新人创作礼」活动,一起开启掘金创作之路。
hook
什么是hook
Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数
useState
useState和useReduce 作为能够触发组件
重新渲染的hooks,我们在使用useState的时候要特别注意的是,useState派发更新函数的执行,就会让整个function组件从头到尾执行一次,所以需要配合useMemo,usecallback等api配合使用
useState 函数返回 一个state状态(变量)和一个改变状态的函数
useState(0) 参数为state状态的初始值。
const [count,setCount] = useState(18);
//此时 count = 18;
setCount 和 类组件中的setState的区别:setState 会合并新旧state ,setCount不会
useEffect
useEffect 就是一个 Effect Hook,给函数组件增加了操作副作用的能力。它跟 class 组件中的 componentDidMount、componentDidUpdate 和 componentWillUnmount 具有相同的用途,只不过被合并成了一个 API
//相当于componentDidMount 和componentDidUpdata
useEffect({
//逻辑操作
})
useEffect Hook 看作 componentDidMount componentDidUpdate 和 componentWillUnmount 这三个函数的组合
如果你的 effect 返回一个函数,React 将会在执行清除操作时调用它
方便取消订阅等在componentWillUnMount的清除操作
useEffect(()=>{
//订阅
obj.subscrible();
return ()=>{
//取消订阅
obj.unsubscrible()
}
})
useEffect 如果不添加限制调教,每次
state更新或者props更新时都会重新调用Effect函数,相当于componentDidUpdate和componentwillreceiveprops给
uesEffect添加合理的限制:通过useEffect的第二个参数来实现。这里说是限定条件,也可以说是上一次useeffect更新收集的
某些记录数据变化的记忆,在新的一轮更新,useeffect会拿出之前的记忆值和当前值做对比,如果发生了变化就执行新的一轮useEffect的副作用函数,useEffect第二个参数是一个数组,用来收集多个限制条件 。(引用自:我不是外星人)
当useEffect函数的第一个参数中的变量发生改变时就调用该useEffect的副作用函数
- 当为
空数组即相当于时componentDidMounted
useEffect 不支持async 和 await 的语法糖
使用 async 需要在useEffect外包一层函数
错误用法
useEffect中的第一个回调参数返回的是一个clean-up函数,所以不能返回promise对象,更不能直接使用async/await
//错误用法
useEffect(async ()=>{
await ...
},[])
包裹一层函数
const asyncEffect = (callback, deps)=>{
useEffect(()=>{
await callback()
},deps)
}
在回调函数内部使用立即执行函数
useEffect(()=>{
//立即执行函数
(async function fn(){
await ...
})
},[])
在回调函数外部定义一个async函数,在回调函数内部调用该函数
useEffect 执行顺序 组件更新挂载完成 -> 浏览器dom 绘制完成 -> 执行useEffect回调 useLayoutEffect 执行顺序 组件更新挂载完成 -> 执行useLayoutEffect回调-> 浏览器dom 绘制完成
useLayoutEffect 会阻塞浏览器的渲染
useRef
获取元素
const dom = useRef();
//通过dom元素的ref标签获取dom元素
<h1 ref={dom}>
//通过dom.current能获取到该元素
缓存数据(高阶用法)
优势:不会像useState那样数据改变更新组件,也不会像定义变量一样,更新就重置。
//初始数据
let ref = useRef(initValue)
//改变数据
ref.current = newValue
解决hook的异步问题
useRef 创建一个不受组件刷新而影响的变量。同高阶用法相同
useContext
useContext
通过useContext可以获取到父组件最近的一个Provider的value
useContext 可以代替 context.Consumer 来获取Provider中保存的value值
Context
Provider的value发生变化是,他的所有子级消费者都会rerender。
简化代码,不需要用用消费者组件包裹消费组件
让你不使用组件嵌套就可以订阅 React 的 Context。
不使用useContext
let Mycontext = React.createContext();
//生产数据
<Mycontext.Provider value={...data}>
父组件
<Father>
.....
<Mycontext.Consumer>
{ //基于context的值进行渲染进行渲染
value =>(
)
}
</Mycontext.Comsumer>
</Father>
<\Mycontext.Provider>
使用useContext
可以不使用消费组件,获取context的数据
const value = useContext(Mycontext)
useReducer
reducer
reducer 是一个函数`(state,action)=>{}
state传入的状态,action触发状态的更新操作
reducer是一个纯函数
state
reducer处理的state对象必须是immutable,这意味着永远
不要直接修改参数中的state对象,reducer函数应该每次都返回一个新的state object
既然reducer要求每次都返回一个新的对象,我们可以使用ES6中的解构赋值方式去创建一个新对象,并复写我们需要改变的state属性
当个state是一个多重嵌套的对象时,使用immer等immutable库处理、
action
一个常规的action对象,有两个属性,type和payload(可选)
type:本次操作的类型,reducer条件判断的依据
payload:(可选)本次操作携带的数据
useReducer
const [state, dispatch] = useReducer(reducer, initState);
useReducer接受两个参数,第一个参数reducer函数,第二个参数state的初始值
useReducer返回一个数组[state,dispatch]
state: 为的新的state,
dispatch: 用来触发reducer函数,dispatch的参数为action对象
function demo(){
const initalValue = {
nickName:'hacker',
age:18
}
function myReducer = (state,action){
switch (action.type){
case 'grow' :
return {...state,age:state.age++};
case 'editNickName' :
return {...state,nickName:state.nickName=action.Nickname};
default:
return state;
}
}
const [state,dispatch] = useReducer(myReducer,initalValue);
//调用dispatch 修改state state 改变组件重新render
dispatch({
type:'editNickName',
payload:{
nickName:'jack'
}
})
}
useREducer+useContext
如何利用
context去解决我们问中开头提到的子孙类组件出发reducer状态变化。没错,就是将dispatch函数作为context的value,共享给页面的子组件。
const MyContext = useContext(initalValue)
//将`dispatch函数`作为`context的value`,共享给页面的子组件。
<MyContext.Provider value= {dispatch}>
....
</<MyContext.Provider>
function Children(){
const dipatch = useContext(MyContext);
//使用dispatch
}
useMemo
无状态组件的更新是整个全部更新,使用useMemo可以实现一部分视图更新,从而达到性能优化
Memo
Memo是一个高阶函数,作用和类组件中的React.pureCmponent类似,用于函数组件
引用:我们先来说一说, memo, 我们知道class声明的组件可以用componentShouldUpdate来限制更新次数,那么memo就是无状态组件的
ShouldUpdate, 而我们今天要讲的useMemo就是更为细小的ShouldUpdate单元
React.memo()可接受2个参数,第一个参数为纯函数的组件,第二个参数用于对比props控制是否刷新,与
shouldComponentUpdate()功能类似。[2]
import React from "react";
function Child({seconds}){
console.log('I am rendering');
return (
<div>I am update every {seconds} seconds</div>
)
};
function areEqual(prevProps, nextProps) {
if(prevProps.seconds===nextProps.seconds){
return true
}else {
return false
}
}
export default React.memo(Child,areEqual)
useMemo
useMemo 接受两个参数,一个是回调函数,另一个是依赖项。
当依赖项发生改变时(即依赖的数据发送改变时),会触发回调函数的调用,从而获取到回调函数的新的返回值
当依赖项没有发生改变时,就会返回之前的缓存值。
const MemoDemo = useMemo(()=>{
.......
},[])
const info = useMemo(()=>{
return {
name:'',
age:count
}
})
<UserInfo userInfo={info}/>
useCallback
和useMemo类似,接受两个参数
不同的是userMemo返回的是函数的返回值,useCallback返回的是函数本身
当依赖项发生改变时,返回一个新的函数。
props传入的回调函数在父组件发生更新时,会生成一个新函数。
const handleClick = useCallback(() => {
setCount(count + 1)
}, [count]);