文章目录
1. useRef的使用以及它和createRef的区别
1.1 useRef介绍
useRef 跟 createRef 类似,都可以用来生成对 DOM 对象的引用,useRef 默认只能在 html 标签中使用,不可以在函数组件中使用。
useRef 和 createRef 的区别:
-
createRef 它可以用在类组件和函数组件中,声明时不能给初始值
useRef 它只能使用在函数组件中,useRef 它可以在声明时给初始值
const usernameCreateRef = createRef() const usernameUseRef = useRef(null)
-
createRef 每次重新渲染时都会创建一个新的 ref 对象,但是类组件中由于生命周期的存在,所以它可以不重新创建(可以将 createRef 写在类组件的构造函数中),但是它在函数组件中就没有这种效果了,它会被重复创建,由此导致性能低下。
useRef 第1次渲染时创建一个对象之后,再新渲染时,如果发现这个对象已经存在过就不会再创建,它的性能更好一些,在函数组件中推荐使用 useRef。
1.2 createRef在函数组件中的使用
import React, {
createRef, useRef, useEffect, useState } from 'react'
// createRef 它可以用在类组件和函数组件中
// useRef 它只能使用在函数组件中
const App = () => {
const usernameRef = createRef()
return (
<div>
<input type="text" ref={
usernameRef} />
<button
onClick={
() => {
console.log(usernameRef.current.value)
}}
>
获取表单项中的数据
</button>
</div>
)
}
export default App
1.3 useRef 在函数组件中的使用
上文提到,在函数组件中 createRef 会被重复创建,而 useRef 不会,我们现在来验证一下这个结论:
import React, {
createRef, useRef, useEffect, useState } from 'react'
// createRef每次重新渲染时都会创建一个ref对象,类组件中它有生命周期,所以它也不会重新创建
// useRef第1次渲染时创建一个对象之后,再新渲染时,如果发现这个对象已经存在过不会再创建,它的性能更好一些,在函数组件中推荐使用useRef
const App = () => {
// 此两个ref对象就是用来验证ref是否在函数组件中重复创建的区别
const usernameCreateRef = createRef()
const usernameUseRef = useRef()
const [count, setCount] = useState(100)
// console.log('usernameCreateRef',usernameCreateRef);//null
// console.log('usernameUseRef',usernameUseRef);//undefined
// 每次渲染usernameCreateRef都是一个新的空对象,所以每次都要执行条件语句中的代码
if (!usernameCreateRef.current) {
console.log('createRef')
usernameCreateRef.current = count
}
// 如果usernameUseRef在第一次时创建的对象存在则不执行条件语句中的代码
// 也就是说,如果你第1次创建成功后,第2次渲染时就会使用之前的对象,条件就不成立了,也就不会执行了
if (!usernameUseRef.current) {
console.log('useRef')
usernameUseRef.current = count
}
return (
<div>
<h3>useState中的count值:{
count}</h3>
<h3>createRef:{
usernameCreateRef.current}</h3>
<h3>useRef:{
usernameUseRef.current}</h3>
<button
onClick={
() => {
setCount(v => v + 1)
}}
>
+++++++
</button>
</div>
)
}
export default App
2. React.forwardRef
概述:
如果 ref 对象绑定在自定义类组件中,则可以得到当前自定义类组件的实例,从而可以实现组件通信
如果 ref 直接绑定在自定义函数组件中,则不行,因为函数组件没有实例,所以会报错
在函数组件中,如果你需要绑定 ref,则需要通过它提供的一个顶层 api 方法来增强函数组件从而实现函数组件也可以绑定 ref 对象 ,这个方法就是 React.forwardRef。
使用:
import React, {
useRef, forwardRef } from 'react'
// 使用forwardRef方法完成对于函数组件中绑定ref对象
const Child = forwardRef((props, _ref) => {
console.log(_ref)
return (
<div>
<h3 ref={
_ref}>child组件</h3>
</div>
)
})
const App = () => {
let childRef = useRef()
return (
<div>
<Child ref={
childRef} />
<button
onClick={
() => {
console.log(childRef.current)
}}
>
++++
</button>
</div>
)
}
export default App
上面代码的执行顺序是这样的,首先负父组件中创建的 useRef 对象,会通过自定义属性传入到子组件中,然后子组件中的 forwardRef 函数中的 _ref 参数接收了 ref 对象,并将 Dom 对象的引用传回到父组件中:
通过 ref 父子组件间可以传值:
import React, {
useRef, forwardRef } from 'react'
// 使用forwardRef方法完成对于函数组件中绑定ref对象
const Child = forwardRef((props, _ref) => {
// 利用useRef可以初始化,可以在父组件中初始化数据,然后把值传进子组件中
console.log(_ref)
return (
<div>
<h3>child组件</h3>
</div>
)
})
const App = () => {
let childRef = useRef(1000)
return (
<div>
<Child ref={
childRef} />
<button
onClick={
() => {
childRef.current++
console.log(childRef.current)
}}
>
++++
</button>
</div>
)
}
export default App
3. useImperativeHandle
概述:
使用它可以透传 Ref,因为函数组件没有实例,所以在默认自定义函数组件中不能使用 ref 属性,使用为了解决此问题,react 提供了一个 hook 和一个高阶组件完帮助函数组件能够使用 ref 属性。
useImperativeHandle 集合 useRef 和 forwardRef 来模拟给函数组件绑定ref对象来得到子组件中对外暴露出来的方法或数据。
使用:
import React, {
useRef, forwardRef, useState, useImperativeHandle } from 'react'
const Child = forwardRef((props, _ref) => {
// 值
let [name, setName] = useState('张三')
// 方法
const setNameFn = name => setName(name)
// 对象
const userRef = useRef()
// 参数1:就是父组件传过来的ref对象
// 参数2:回调函数,返回一个对象,穿透给父组件的数据
useImperativeHandle(_ref, () => {
return {
// 给父组件传回了
// 值
name,
// 方法
setNameFn,
// 对象
userRef
}
})
return (
<div>
<h3>child组件 -- {
name}</h3>
{
/* 这里的 ref 是子组件自己的 */}
<input type="text" ref={
userRef} />
</div>
)
})
const App = () => {
let childRef = useRef()
return (
<div>
<Child ref={
childRef} />
<button
onClick={
() => {
console.log(childRef.current.name)
childRef.current.setNameFn(Date.now() + '')
childRef.current.userRef.current.value = 'abc'
}}
>
++++
</button>
</div>
)
}
export default App
上述代码的执行顺序是这样的:
4. useLayoutEffect
概述:
大部分情况下,使用 useEffect 就可以帮我们处理组件的副作用,但是如果想要同步调用一些副作用,比如对 DOM 的操作,就需要使用 useLayoutEffect,useLayoutEffect 中的副作用会在 DOM 更新之后同步执行。
useEffect 是异步的,而 useLayoutEffect 是同步的。
使用:
import React, {
useLayoutEffect, useEffect } from 'react'
const App = () => {
// js事件循环中,同步优先于异步
// 异步
useEffect(()=>{
console.log('useEffect');
},[])
// 同步 如果你是操作dom来说,建议用它
useLayoutEffect(()=>{
console.log('useLayoutEffect');
document.title = '你好react'
},[])
return <div>
<h3>App组件</h3>
</div>
}
export default App