为什么用ref
在如下代码中,log会打印什么?
function App() {
const [count, setCount] = useState(0)
let num = 0
const handelClick = () => {
setCount(count + 1)
num++
console.log(num)
}
return (
<>
<button onClick={handelClick}>点击</button>
{count}
</>
)
}
每次点击按钮都会重新触发函数的执行,由于num并没有记忆功能,每次执行函数都会从新建作用域中找num的值,则初始值都为0,故log打印‘1’. 通过引入react的内置hook中useRef钩子函数让num记忆。而与useState不同的是ref返回的是对象,用num.current的语法拿到当前值. 更改后的代码
let num = useRef(0)
const handelClick = () => {
setCount(count + 1)
num.current++
console.log(num.current)
}
因为useRef根本就没有更新,没有让函数重新走一遍。所以num的值可以直接在useRef()中更改。并且不能 {num.current}
直接渲染到页面中。
useRef和useState不同
使用场景
如想点击按钮后加入定时器,当用户点击多次时会触发多个定时器虽然可以用 clearInterval()把多余的定时器清掉,一旦调用当前组件就无法清理。所以用ref保证不管在哪种作用域中都能记忆值。、
const time = useRef(null)
const handelClick = () => {
clearInterval(time.current)
time.current = setInterval(() => {
console.log(334)
}, 1000)
}
一旦重复点击按钮,会先清理上一次的值,开启新的定时器也从初始值开始记忆。
操作DOM的用法:
为防止每次执行组件时重新执行DOM,用ref防止重新操作DOM
用对象的方式ref={myref}将DOM与ref关联起来,myref.current拿到DOM
function App() {
const myref = useRef(null)
const handelClick = () => {
console.log(myref.current.innerHTML)
myref.current.style.backgroundColor = 'blue'
}
return (
<>
<button ref={myref} onClick={handelClick}>
点击
</button>
</>
)
}
export default App
用遍历操作DOM时不能在map()方法内部声明ref const myref = useRef(null)再与DOM关联ref={myref} ,因为函数是从上往下执行,这样做会报错。所以应该采用回调函数的写法 ref={(myref) => { myref.style.backgroundColor = 'yellow' }}
function App() {
const myref = useRef(0)
const list = [
{ id: 1, text: 'qqq' },
{ id: 2, text: 'bbb' },
{ id: 3, text: 'www' },
]
return (
<>
{list.map((item) => {
return (
<li
key={item.id}
ref={(myref) => {
myref.style.backgroundColor = 'yellow'
}}
>
{item.text}
</li>
)
})}
</>
)
}
export default App
forwardRef转发组件
组件不同于DOM元素,不能直接用ref={ref}绑定获取,需要用forwardRef转发。
const Myinput = forwardRef(function Myinput(props, ref) {
return (
<>
<input type="text" ref={ref} />
</>
)
})
function App() {
const myref = useRef(null)
const handelClick = () => {
myref.current.focus()
myref.current.style.background = 'yellow'
}
return (
<>
<button onClick={handelClick}>点击</button>
<Myinput ref={myref} />
输入
</>
)
}
export default App
useImperativeHandle定义ref暴露
用forwardRef后ref被暴露出来可以任由改动,useImperativeHandle钩子函数用于定义ref想要暴露出来允许改动的方法,样式等。这个钩子接收两个参数:一个 ref,以及一个返回对象,该对象包含了可以被父组件通过 ref 调用的方法。
const Myinput = forwardRef(function Myinput(props, ref) {
const inputref = useRef(null)
useImperativeHandle(ref, () => {
return {
focus() {
return inputref.current.focus
},
interoptions() {
inputref.current.style.backgroundColor = 'yellow'
},
}
})
return (
<>
<input type="text" ref={inputref} />
</>
)
})
function App() {
const myref = useRef(null)
const handelClick = () => {
myref.current.focus()
myref.current.interoptions()
}
return (
<>
<button onClick={handelClick}>点击</button>
<Myinput ref={myref} />
输入
</>
)
}
export default App