携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第24天,点击查看活动详情
前言
由vue3转学react,之前从未接触过react,自学了一遍感觉还好,容易上手。 期间的一些笔记,后续有更深的见解持续更新。
hooks的一些API
本质:让函数组件更强大更灵活的钩子,只能在函数组件(有状态)中使用。
作用:组件状态复用,class组件自身问题
注意:只能写在函数组件最外层,不能写在if,for循环判断里面(官文文档解释为react的运行机制要保持这些hooks的顺序的唯一性),可以将回调函数作为参数传递
useState
const [count, setCount] = useState(initCount);
useState传入的是当前count初始值也就是initCount(可以是初始值也可以是函数),然后返回的是最新的count,和一个修改count的方法(可以普通调用和函数调用,,数据是对象类型的话,一般要结合拓展运算符复制一份)。
//三种情况
setCount(count + 1)
setCount(() => count + 1)
setCount(() => {
return{
...count,
//再对象赋值
}
})
初始值initCount只有在首次渲染生效,后续更新会被忽略,直接调用setCount
一句话:在同步里,异步更新状态和dom,在异步里,同步更新状态和dom。
useEffect:为更好处理副作用(除了更新UI外的作用,如ajax,本地存储)
感觉像vue的nextTick。
React会在每次渲染完后调用useEffect,包括第一次加载渲染DOM。
useEffect 设计初衷是用来取代 componentDidMount 和 componentDidUpdate,它接收两个参数(fun,[]),一个处理函数,另一个关联的状态或数组,这个变了就重新执行。
加 [ ] 为行业只执行义一次的默认写法,一般用于发送请求:
useEffect(() => {
async function sendData() {
const res = await fetch('http://www.baidu.com')
.then(response => response.json())
.then(data => console.log(data))
}
sendData()
},[])
而 useLayoutEffect 的作用和 useEffect 几乎差不多,几乎看不到任何差别,但它们的渲染底层逻辑就稍微不同。
具体可见这篇文章,用一个例子很清楚讲明,在性能优化上尽量选用useEffect,在解决页面渲染更新会闪烁可以用useLayoutEffect。
useRef:获取实例dom或组件方法,必须是类组件
步骤:导入useRef函数,通过ref绑定要获取的元素,执行useRef并传入null,这将会返回一个含有current属性的对象。
import React, { useRef, useEffect } from 'react'
//app组件里绑定
<UseRef1 ref={hook} />
<p ref={pRef}>useRef学习,标签实例</p>
//获取
const pRef = useRef(null)
const hook = useRef(null)
useEffect(() => {
console.log(pRef.current)
console.log(hook.current.state.name)
}, [])
useContext:祖孙组件响应式更新
一个例子直接说明梗概。
import React, { createContext, useContext, useState } from 'react'
//1.创建上下文
const Context = createContext()
//2.在顶层app组件上,使用Providr包裹孙组件,value传值
const [count,setCount]=useState(10)
<Context.Provide value={count}>
<div>
<Son />
<button onClick={()=>{setCount(count+1)}}></button>
</div>
</Context.Provider>
//3.在son组件里调用useContext方法,Context和createContext保存的变量一致
const count = useContext(Context)
//这样孙组件就可以显示或同步app组件的值
useReducer
const [ state,dispatch ] = useReducer(reducer, initState, init)
接收三个参数,分别为:处理状态更新的reducer,状态初始值,状态初始化函数。
有两种传递方式:不传第三个参数,如1;state数据比较复杂,可以将第二个参数作为第三个参数传入,如2。
最后,useReducer将返回两个东西,一个当前最新的状态state,一个是更新状态的dispatch。
//1.将状态初始值作为第二个参数传入
const [state, dispatch] = useReducer(
reducer,
{count: initState}
)
//2.惰性初始化,将第二个参数作为第三个函数传入
function init(initState){
return {count: initState}
}
function renducer(state,action){
switch{
case "change":
return {count: state.count - 1}
case "reset":
return init(ac)
//...
}
}
function Counter({initState}){
const [ state,dispatch ] = useReducer(reducer, initState, init)
return(
<>
<button onClick={() => dispath({type: 'change',count: initState})}></button>
</>
)
}
useCallback
入参和useEffect,接收两个参数(fun,[]),一个处理函数,另一个关联的状态或数组,这个变了就重新执行。
那为啥要用useCallback:那是因为在函数组件里的函数,会随着状态值的更新而重新渲染,函数也会频繁被定义且组件通信很耗性能。使用useCallback+memo可以解决上述问题
useCallback(fn, deps) 相当于 useMemo(() => fn, deps)。
useMemo(相当于vue的computed)
入参和useEffect,接收两个参数(fun,[]),一个处理函数,另一个关联的状态或数组,这个变了就重新执行。如果没有提供依赖项数组,useMemo 在每次渲染时都会计算新的值。
官方建议:不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于 useEffect 的适用范畴,而不是 useMemo。
useCallback + useMemo
痛点见上述
//使用Memo后确实不会影响了,但父组件传值过来呢
const Son = React.memo(function Son(){
...
})
//父组件:这样就不会
const childClick = useCallback(() => {}.[])
<Son click={childClick} />
最后
持续更新中...