useState的用法很简单,返回一个数组,数组的值为当前state和更新state的函数;
useState的参数是变量、对象或者是函数,变量或者对象会作为state的初始值,如果是函数,函数的返回值会作为初始值。
function Count(){
let [count,setCount] = useState(0)
const handleAdd = function(){
setCount(count+1)
setCount(count+1)
}
return(
<div>
<p>{count}</p>
/*每次点击加1*/
<button onClick={handleAdd}>加一</button>
</div>
)
}
在同一个事件中并不会因为调用了两次setCount
而让count
增加两次,试想如果在同一个事件中每次调用setCount
都生效,那么每调用一次setCount
组件就会重新渲染一次,这无疑使非常影响性能的;实际上如果修改的state
是同一个,最后一个setCount
函数中的新state
会覆盖前面的
这两个hook
用法都一致,都是处理副作用的hook函数。第一个参数是回调函数,第二个参数是个数组。数组的内容是依赖项deps
,依赖项改变后执行回调函数;注意组件每次渲染会默认执行一次,如果不传第二个参数,则只要该组件有state(状态)改变就会触发回调函数;如果传一个空数组,则只会在初始化时执行一次。如果在回调函数中return
返回了一个函数,则在组件销毁时调用,同时组件每次重新渲染的时候都会先执行该函数再调用回调函数。
hook
执行时机不同 useLayoutEffect
是在页面渲染前执行,是同步调用。
useEffect
是在页面渲染后执行,是异步调用。
视情况而定,如果回调函数会修改state导致组件重新渲染,可以使用useLayoutEffect
,因为这个时候用useEffect
可能会造成页面闪烁;如果回调函数中去请求数据或者执行时间过长,建议使用useEffect
;因为这个时候使用useLayoutEffect
会造成堵塞浏览器渲染。
React.memo
和PureComponent
作用类似,可以用作性能优化,React.memo
是高阶组件,函数组件和类组件都可以使用, 和区别PureComponent
是 React.memo
只能对props
的情况确定是否渲染,而PureComponent
是针对props
和state
。
React.memo
接受两个参数,第一个参数原始组件本身,第二个参数,可以根据一次更新中props
是否相同决定原始组件是否重新渲染。是一个返回布尔值,true
证明组件无须重新渲染,false
证明组件需要重新渲染,这个和类组件中的shouldComponentUpdate()
正好相反 。
React.memo: 第二个参数 返回 true
组件不渲染 , 返回 false
组件重新渲染。 shouldComponentUpdate: 返回 true
组件渲染 , 返回 false
组件不渲染。
接下来我们做一个场景,控制组件在仅此一个props
数字变量,一定范围渲染。
例子🌰:
控制 props
中的 number
:
number
更改,组件渲染。number
小于 5 ,组件渲染。function TextMemo(props){
console.log('子组件渲染')
if(props)
return <div>hello,world</div>
}
const controlIsRender = (pre,next)=>{
if(pre.number === next.number ){ // number 不改变 ,不渲染组件
return true
}else if(pre.number !== next.number && next.number > 5 ) { // number 改变 ,但值大于5 , 不渲染组件
return true
}else { // 否则渲染组件
return false
}
}
const NewTexMemo = memo(TextMemo,controlIsRender)
class Index extends React.Component{
constructor(props){
super(props)
this.state={
number:1,
num:1
}
}
render(){
const { num , number } = this.state
return <div>
<div>
改变num:当前值 { num }
<button onClick={ ()=>this.setState({ num:num + 1 }) } >num++</button>
<button onClick={ ()=>this.setState({ num:num - 1 }) } >num--</button>
</div>
<div>
改变number: 当前值 { number }
<button onClick={ ()=>this.setState({ number:number + 1 }) } > number ++</button>
<button onClick={ ()=>this.setState({ number:number - 1 }) } > number -- </button>
</div>
<NewTexMemo num={ num } number={number} />
</div>
}
}
效果:
memo.gif
完美达到了效果,React.memo
一定程度上,可以等价于组件外部使用shouldComponentUpdate
,用于拦截新老props
,确定组件是否更新。
useMemo
接收两个参数,第一个参数是一个函数,返回值用于产生保存值,第二个参数是一个数组,作为dep依赖项。当数组里面的依赖项发生变化,重新执行第一个函数,产生新的值。
1.缓存一些值,避免重新执行上下文
const number = useMemo(()=>{
/** 大量的逻辑运算**/
return number
},[props.number])
//只有props的number改变时,重新计算number的值
2 减少不必要的dom
循环
/* 用 useMemo包裹的list可以限定当且仅当list改变的时候才更新此list,这样就可以避免selectList重新循环 */
{useMemo(() => (
<div>{
selectList.map((i, v) => (
<span
className={style.listSpan}
key={v} >
{i.patentName}
</span>
))}
</div>
), [selectList])}
3 减少子组件渲染
/* 只有当props中,list列表改变的时候,子组件才渲染 */
const goodListChild = useMemo(()=> <GoodList list={ props.list } /> ,[ props.list ])
useMemo
和 useCallback
接收的参数都是一样,都是在其依赖项发生变化后才执行,都是返回缓存的值,区别在于 useMemo
返回的是函数运行的结果, useCallback
返回的是函数。 返回的callback
可以作为props
回调函数传递给子组件。
缓存函数,当页面重新渲染render时,依赖项不变时,不会去重新生成这个函数。
子组件使用React.memo包裹,父组件需要传递至子组件的函数使用useCallback缓存,来避免子组件不必要的重新render。当传给子组件函数时。子组件的memo判断会出问题,传递进去的函数都是重新创建的,引用地址前后不一致。所以当用useCallback去缓存这个函数,则memo判断则不会有问题,会正常缓存
/* 用react.memo */
const DemoChildren = React.memo((props)=>{
/* 只有初始化的时候打印了 子组件更新 */
console.log('子组件更新')
useEffect(()=>{
props.getInfo('子组件')
},[])
return <div>子组件</div>
})
const DemoUseCallback=({ id })=>{
const [number, setNumber] = useState(1)
/* 此时usecallback的第一参数 (sonName)=>{ console.log(sonName) }
经过处理赋值给 getInfo */
const getInfo = useCallback((sonName)=>{
console.log(sonName)
},[id])
return <div>
{/* 点击按钮触发父组件更新 ,但是子组件没有更新 */}
<button onClick={ ()=>setNumber(number+1) } >增加</button>
<DemoChildren getInfo={getInfo} />
</div>
}
useRef
的作用:
dom
元素,或者class
组件实例 。useRef
时候,会创建一个原始对象,只要函数组件不被销毁,原始对象就会一直存在,那么我们可以利用这个特性,来通过useRef
保存一些数据。const DemoUseRef = ()=>{
const dom= useRef(null)
const handerSubmit = ()=>{
/* <div >表单组件</div> dom 节点 */
console.log(dom.current)
}
return <div>
{/* ref 标记当前dom节点 */}
<div ref={dom} >表单组件</div>
<button onClick={()=>handerSubmit()} >提交</button>
</div>
}