目录
使用 useRef 操作DOM
使用场景:在 React 中进行 DOM 操作时,用来获取 DOM
作用:返回一个带有 current 属性的可变对象,通过该对象就可以进行 DOM 操作了。
const inputRef = useRef(null)
解释:
-
参数:在获取 DOM 时,一般都设置为 null
-
返回值:包含 current 属性的对象。
-
注意:只要在 React 中进行 DOM 操作,都可以通过 useRef Hook 来获取 DOM(比如,获取 DOM 的宽高等)。
-
注意:useRef不仅仅可以用于操作DOM,还可以操作组件
示例代码
// 第一步,引入useRef
import { useRef } from 'react'
import reactDom from 'react-dom'
export default function App () {
// 第二步,调用useRef,并设置一个空的初始值,得到引入对象
const inpRef = useRef(null)
const changeInp = () => {
// 第四部,通过引入对象.current属性,获得元素或组件
console.log(inpRef.current)
inpRef.current.style.backgroundColor = 'red'
inpRef.current.focus()
}
return (
<div>
{/* 第三步,把引入对象设置ref给任意元素或组件 */}
<input ref={inpRef}></input>
<button onClick={changeInp}>按钮</button>
</div>
)
}
reactDom.render(<App></App>, document.querySelector('#root'))
useRef-在多次渲染之间共享数据
问题导入
import React, { useEffect, useState } from 'react'
import ReactDom from 'react-dom'
export default function App () {
const [count, setCount] = useState(0)
let timeId = null
console.log(timeId)
useEffect(() => {
timeId = setInterval(() => {
setCount((count) => count + 1)
}, 1000)
console.log(timeId)
}, [])
const hClick = () => {
console.log(timeId)
clearInterval(timeId)
}
return (
<div>
count:{count}
<button onClick={hClick}>点击停止定时器</button>
</div>
)
}
ReactDom.render(<App />, document.getElementById('root'))
分析问题
setCount会导致组件重新渲染,而重新渲染时,会重复执行如下代码
let timeId = null
所以,无法保存timeId
解决方法
将timeId保存在一个多次渲染也不会丢失的位置。
// 1. 导入 useRef
import React, { useRef } from 'react'
组件() {
// 2. 调用useRef,
const timeRef = useRef(null)
// 3. 向引用的current中存入数据
timeRef.current = 你需要在多次渲染之间共享的数据
}
import React, { useEffect, useRef, useState } from 'react'
import ReactDom from 'react-dom'
export default function App () {
const [count, setCount] = useState(0)
const timeRef = useRef(null)
console.log(timeRef.current)
useEffect(() => {
timeRef.current = setInterval(() => {
setCount((count) => count + 1)
}, 1000)
console.log(timeRef.current)
}, [])
const hClick = () => {
console.log(timeRef.current)
clearInterval(timeRef.current)
}
return (
<div>
count:{count}
<button onClick={hClick}>点击停止定时器</button>
</div>
)
}
ReactDom.render(<App />, document.getElementById('root'))
自定义hooks-封装倒计时-需求-验证码
import { useState, useEffect, useRef } from 'react'
import reactDom from 'react-dom'
function App () {
const [flag, setFlag] = useState(false)
const [count, setCount] = useState(0)
const btnRef = useRef(null)
// 组件销毁时,触发 componentWillUnmount,清除定时器
useEffect(() => {
return () => {
clearInterval(btnRef.current)
console.log('组件已毁,玉石俱焚')
}
}, [])
// 监听count,如果为0,执行回调
useEffect(() => {
if (count === 0) {
clearInterval(btnRef.current)
setFlag(() => false)
}
console.log(count)
}, [count])
// 点击按钮执行事件
const changeState = () => {
// 1.初始化定时器
setCount(5)
// 2.按钮变为禁用
setFlag(() => { return true })
// 3.开启倒计时
btnRef.current = setInterval(() => {
setCount((count) => {
return count - 1
})
}, 1000)
}
return (
<div>
<button disabled={flag} onClick={changeState} >
{flag ? count + 's后重新获取' : '点击获取验证码'}
</button>
</div>
)
}
export default function Box () {
const [show, setShow] = useState(true)
return (
<div>
{show && <App></App>}
<button onClick={() => { setShow(false) }}>销毁</button>
</div>
)
}
reactDom.render(<Box></Box>, document.querySelector('#root'))
自定义hooks-封装倒计时-需求-404页面3秒跳转
import { useState, useEffect, useRef } from 'react'
import reactDom from 'react-dom'
export default function App () {
// 设置初始值
const [count, setCount] = useState(3)
// 调用useRef,
const timerRef = useRef(null)
// 启动定时器
useEffect(() => {
timerRef.current = setInterval(() => {
setCount((count) => count - 1)
}, 1000)
}, [])
// 定时器到点后,清空并跳转
useEffect(() => {
if (count <= 0) {
clearInterval(timerRef.current)
location.href = 'http://www.baidu.com'
}
}, [count])
// 手动点击,立即清空并跳转
const jump = () => {
clearInterval(timerRef.current)
location.href = 'http://www.baidu.com'
}
return (
<div>
{count + '后跳转到百度'}
<p>
点击
<span style={
{ color: 'blue', borderBottom: '1px solid blue', cursor: 'pointer' }} onClick={jump}>
www.baidu.com
</span>立即跳转
</p>
</div>
)
}
reactDom.render(<App></App>, document.querySelector('#root'))
自定义hooks-封装倒计时-提炼hooks
import { useState, useEffect, useRef } from 'react'
export default function useTimer (init, callback) {
const [count, setCount] = useState(init)
const timerRef = useRef(null)
const state = () => {
// 每次调用从新赋予初始值
setCount(init)
timerRef.current = setInterval(() => {
setCount((init) => init - 1)
}, 1000)
}
// 组件销毁时清除定时器
useEffect(() => {
return () => {
clearInterval(timerRef.current)
}
}, [])
// 如果初始值小于0,清除定时器
useEffect(() => {
// 如果倒计时归0,清除定时器,调用函数
if (count <= 0) {
clearInterval(timerRef.current)
callback()
}
}, [count])
return { count, state }
}
模拟获取验证码
import { useState } from 'react'
import reactDom from 'react-dom'
import useTimer from './useTimer'
function App () {
const [flag, setFlag] = useState(false)
const timer = useTimer(3, () => { setFlag(false) })
// 点击按钮执行事件
const changeState = () => {
// 禁用按钮
setFlag(true)
// 调用函数,启动定时器
timer.state()
}
return (
<div>
<button disabled={flag} onClick={changeState} >
{flag ? timer.count + 's后重新获取' : '点击获取验证码'}
</button>
</div>
)
}
export default function Box () {
const [show, setShow] = useState(true)
return (
<div>
{show && <App></App>}
<button onClick={() => { setShow(false) }}>销毁</button>
</div>
)
}
reactDom.render(<Box></Box>, document.querySelector('#root'))
倒计时跳转页面
import reactDom from 'react-dom'
import { useEffect } from 'react'
import useTimer from './useTimer'
export default function App () {
const { count, state } = useTimer(3, () => { location.href = 'http://www.baidu.com' })
useEffect(() => {
state()
}, [])
return (
<div>{count} 后跳转到百度,或点击<a href="http://www.baidu.com">百度</a>跳转</div>
)
}
reactDom.render(<App></App>, document.querySelector('#root'))