简述
useRef
用于操作不需要在视图上渲染的属性数据,用于访问真实的DOM节点
,或者React组件的实例对象
,允许直接操作DOM元素或者是组件;
写法
const inpRef = useRef(params)
参数:
useRef(params)
,接收的 params
可以是任意类型
的数据,初始值在React首次渲染中会被忽略
返回值:inpRef
,是一个包含 current 属性的对象
,每次修改更新都会返回包含该属性的对象;
1、表单中直接通过 inpRef.current
修改
import { useRef } from 'react'
export default function MyRef() {
const inpRef = useRef(null)
console.log('==render=')
const handleSearch = () => {
console.log('==inpRef==', inpRef)
inpRef.current.value = inpRef.current.value - 0 + 1
console.log('==value=', inpRef.current.value)
}
return (
<div>
<input className="inp" ref={inpRef}></input><button onClick={handleSearch}>搜索</button>
<hr />
<button>{inpRef?.current?.value || '初始值'}</button>
</div>
)
}
通过log日志可以看出来:
render
只有在初始化渲染时候才会打印,而当点击 搜索 按钮进行累加 input的值时候,只有input输入框中的值变化了,button 按钮中的值依然是 “初始值” 三个字;
2、直接操作DOM事件
可以直接访问自身组件中的事件,但是不允许访问其它组件
的事件,即使是子组件的事件也不行
,因为React设计的 Refs
是一种脱围机制
,访问其它组件的事件,会使组件本身变得不那么稳定健壮;可以使用forwardRef
访问到子组件的事件
import { useRef } from 'react'
export default function MyRef() {
const inpRef = useRef(null)
const handleSearch = () => {
console.log('==inpRef==', inpRef)
inpRef.current.focus()
}
return (
<div>
<input className="inp" ref={inpRef}></input><button onClick={handleSearch}>搜索</button>
</div>
)
}
useRef 返回值,可以直接操作input 原生事件,如focus、blur、change
;vidoe视频的播放play、暂停pause
等
3、访问子组件事件
需要使用 forwardRef 和 useImperativeHandle
;
通过useImperativeHandle
在子组件暴露出父组件需要访问的属性或方法,类似与vue3
中的defineExpose()
// 父组件
import { useRef, useEffect } from 'react'
import Child from './child'
export default function MyRef() {
const childRef = useRef(null)
console.log('==render=')
const handleGetChild = () => {
console.log('==childRef==', childRef)
childRef.current.handleChange()
childRef.current.myfocus()
}
return (
<div>
<button onClick={handleGetChild}>获取子组件</button>
<hr />
<Child ref={childRef} ></Child>
</div>
)
}
// 子组件
import { forwardRef, useRef, useImperativeHandle } from 'react'
// 使用 forwardRef 让组件使用 ref 将 DOM 节点暴露给父组件
const ChildInp = forwardRef(({value}, ref) =>{
// 定义当前组件中 input 的ref
const inputRef = useRef(null);
const handleChange = (data) => {
console.log('==handleChange=', data)
}
// 只暴露 父组件需要的属性方法
useImperativeHandle(ref, () => {
console.log('=ref=8888=', inputRef)
return {
handleChange,
myfocus(){
inputRef.current.focus()
}
}
}, [])
return (
<div>
<p>Child 组件:</p>
<input
value={value}
onChange={handleChange}
ref={inputRef}
/>
</div>
)
})
export default ChildInp
用途:
1、直接操作DOM
:可以通过 inpRef
来访问真实DOM,进而操作原生DOM的一些增删改查、颜色位置等操作;
2、访问组件的方法属性:有时我们需要在父组件直接访问子组件的属性方法,可以结合forwardRef 访问到子组件的方法;
3、获取组件的实例或者事件进行监听:
注意事项:
1、inpRef.current
是可以直接修改的,但是它的修改不会触发视图的变更;
2、在视图更新渲染期间,不要尝试读写inpRef.current
,这样会导致组件的行为难以捕捉;可以在 事件处理程序或者 Effect 中读取和写入 ref。
3、inpRef
可以在重新渲染直接存储信息,普通的对象每次重新渲染会将信息重置
4、谨慎使用 useRef 访问DOM的操作,尽可能使用数据驱动操作,触发现有方案无法满足,才使用useRef访问DOM