首先做个对比:
功能一:引用一个值做持久化记忆
场景:清除定时器
import { useRef } from "react"
import { useState } from "react"
// ref:1.引用一个值 做持久化记忆
// let num = useRef(1)
// console.log(num.current++);
// 避免渲染ref的值,其值改变时不会触发更新(区别于state)
const App = () => {
const [count, setCount] = useState(0)
// let timer = null
// const handleClick = () => {
// setCount(count + 1)
// // 不能有效清除定时器,因为每次只能清除当前作用域的timer,但是每次setState都会产生新的作用域,则多次点击会导致定时器累加
// clearInterval(timer)
// timer = setInterval(() => {
// console.log('setInterval...');
// }, 1000);
// }
let timer = useRef(null)
const handleClick = () => {
setCount(count + 1)
// ref具有记忆性,可以拿到第一次产生的timer,然后清除,保证每次只有一个定时器在
clearInterval(timer.current)
timer.current = setInterval(() => {
console.log('setInterval...');
}, 1000);
}
return (<>
hello hook
<button onClick={handleClick}>按钮</button>
<br />
count:{count},
</>)
}
export default App
功能二:在react中操作dom(避免原生写法
const App = () => {
const myRef = useRef(null)
const fun = () => {
console.log(myRef.current.innerHTML);
myRef.current.style.color = 'skyblue'
}
const list = [
{ id: 1, text: 'aaa' },
{ id: 2, text: 'bbb' },
{ id: 3, text: 'ccc' }
]
return (
<>
<button onClick={fun}>点击让div变色</button>
<div ref={myRef}>div</div>
{/* 循环中操作ref可以使用回调写法 */}
<ul>
{
list.map((item) => {
return <li key={item.id} ref={(mf) => { mf.style.background = 'pink' }}>{item.text}</li>
})
}
</ul>
</>
)
}
export default App
注意:当给子组件添加ref时,需要对其 forwardRef 转发,用于向父组件公开其dom
const Zi = forwardRef(function myInput(props, refs) {
return <input type="text" ref={refs} />
})
const App = () => {
const myref = useRef(null)
const fn = () => {
myref.current.focus()
myref.current.style.background = 'skyblue'
}
return (<>
hello App
<button onClick={fn}>点击变色</button>
<Zi ref={myref} />
</>)
}
export default App
进阶:useImperativeHandle用于为组件自定义暴露方法
import { useRef, forwardRef, useImperativeHandle } from "react"
const Zi = forwardRef(function myInput(props, refs) {
const inputRef = useRef(null)
useImperativeHandle(refs, () => {
return {
myFocus() {
inputRef.current.focus()
},
focusAndStyle() {
inputRef.current.focus()
inputRef.current.style.background = 'red'
}
}
})
return (
<input type="text" ref={inputRef} />
)
})
const App = () => {
const myref = useRef(null)
const fn = () => {
myref.current.myFocus()
myref.current.focusAndStyle()
// 以下会报错 myref.current.focus is not a function,因为只有以上俩方法暴露了
// myref.current.focus()
}
return (<>
hello App
<button onClick={fn}>点击</button>
<Zi ref={myref} />
</>)
}
export default App