共计 3774 个字符,预计需要花费 10 分钟才能阅读完成。
“ 对 react 的自带 hooks 进行的演绎总结 ”
react-hooks:
常见,useState、useEffect、useMemo、useRef、useCallback、useContext
少见,useReducer、useImperativeHandle、useDebugValue、useTransition、useLayoutEffect
useState
因为函数式组件没有 this 与 state,无奈间接批改 state,因而 useState 是 react 对外裸露的一个对 state 操作的钩子函数。
该函数可接管一个参数,是为变量的默认值,如果不写的话,变量默认为 undefined
。
useState 返回一个数组,数组的第一位是变量名,第二位是扭转该变量的函数,查看源码,会发现这个函数是一个 dispatch 函数(异步),所以间接查看该变量失去的是原先的值,如果要查看该变量值,配合应用useEffect
去监听。
扭转变量的函数有一个参数,应用的时候,间接放值类型,不可是表达式等。
const [number, setNumber] = useState(0) /* 0 为初始值 */
return (<div>
<span>{number}</span>
<button onClick={()=> {setNumber(number+1) /* 写法一 */
setNumber(number=>number + 1) /* 写法二 */
console.log(number) /* 这里的 number 是不可能即时扭转的 */
} } >num++</button>
</div>)
useEffect
函数式组件也没有生命周期,用 useEffect 取代生命周期。同时对应前文所讲,useState 批改的值,在这里是能够取到的,实现数据的交互。useEffect 也叫副作用,顾名思义,是在渲染实现后执行。理论应用中 useEffect 必须填写第二个参数来应用,否则这可能会导致性能问题,比方两次渲染的数据齐全一样。
第一个参数是 effect 函数,第二个参数是依赖项,是一个数组,如果是一个空数组,useEffect 不依赖于 props 或 state 中的任何值,所以它永远都不须要反复执行,该钩子会实现 componentDidMount
生命周期函数的性能,即页面加载时触发一次。
如果不是空数组,就要增加 useState 中的变量,这个变量每次发生变化就会触发 useEffect,即componentDidUpdate
,同时也包含componentDidMount
。
const [num, setNum] = useState(1)
useEffect(() => {console.log('componentDidUpdate')
}, [num])
useEffect(() => {console.log('componentDidMount')
}, [])
return (
<>
<div>{num}</div>
<button onClick={() => { setNum(num + 1) }}> 点击 </button>
</>
)
数组中能够放多个 useState 的值,每个值发生变化都会触发,理论开发中,这个依赖项数组只放一个变量。第二个参数个别状况下不要应用援用类型,因为比对的是浅比拟,援用类型的指针地址没有变动的话,进入不到 useEffect 中。
useEffect 第一个 effect 函数参数中还能够返回一个函数,示意解绑副作用,比方在 effect 函数中有个定时器,在组件销毁时须要革除定时器,此时要解绑,就能够在 effect 函数中返回一个函数,这个函数相当于 componentWillUnmount
生命周期。在应用中,从新渲染依赖项,也会先执行解绑副作用,在进行副作用,即 effect 函数。
const [num, setNum] = useState(1)
useEffect(() => {console.log('componentDidUpdate')
return ()=>{console.log('componentWillUnmount')
}
}, [num])
return (
<>
<div>{num}</div>
<button onClick={() => { setNum(num + 1) }}> 点击 </button>
</>
)
useMemo
与 useEffect 类似,参数也是一个函数与一个依赖项数组,次要作用是有助于防止在每次渲染时都进行高开销的计算。
它仅会在某个依赖项扭转时才从新计算值,这个过程会在页面渲染时进行,类比生命周期就是 shouldComponentUpdate
。
如果没有提供依赖项数组,useMemo 在每次渲染时都会计算新的值。不要在这个函数外部执行与渲染无关的操作,诸如副作用这类的操作属于 useEffect 的实用领域,而不是 useMemo。
useMemo 会返回一个 memo 函数的后果 return () => name
,用定义的变量去接管并应用该定义的变量。
与 useEffect 的比拟:
useMemo: 渲染期间管制,值 / 函数的触发 / 扭转
useEffect: 渲染完结后管制,值 / 函数的触发 / 扭转
// 产品名称列表
const nameList = ['apple', 'peer', 'banana', 'lemon']
const example = (props) => {
// 产品名称、价格
const [price, setPrice] = useState(0)
const [name, setName] = useState('apple')
function getProductName() {console.log('getProductName 触发')
return name
}
// 只对 price 响应
useEffect(() => {console.log('price effect 触发')
}, [price])
// 只对 name 响应
useEffect(() => {console.log('name effect 触发')
getProductName()}, [name])
// memo 化的 getProductName 函数
const memo_getProductName = useMemo(() => {console.log('name memo 触发')
return () => name // 返回一个函数}, [name])
return (
<Fragment>
<p>{name}</p>
<p>{price}</p>
<p> 一般的 name:{getProductName()}</p>
<p>memo 化的:{memo_getProductName()}</p>
<button onClick={() => setPrice(price + 1)}> 价格 +1</button>
<button onClick={() => setName(nameList[Math.random() * nameList.length << 0])}> 批改名字 </button>
</Fragment>
)
}
useMemo 也能够包裹标签与子组件
/* 用 useMemo 包裹的 list 能够限定当且仅当 list 扭转的时候才更新此 list,这样就能够防止 selectList 从新循环 */
{useMemo(() => (
<div>{selectList.map((i, v) => (
<span
className={style.listSpan}
key={v} >
{i.patentName}
</span>
))}
</div>
), [selectList])}
/* 只有当 props 中,list 列表扭转的时候,子组件才渲染 */
const goodListChild = useMemo(()=> <GoodList list={ props.list} /> ,[props.list])
useCallback
useMemo 和 useCallback 接管的参数都是一样,都是在其依赖项发生变化后才执行,都是返回缓存的值,区别在于 useMemo 返回的是函数运行的后果,useCallback 返回的是函数。返回的 callback 能够作为 props 回调函数传递给子组件。
/* 用 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>
}