React函数组件Hooks-useRef&forwardRef详解

4,068 阅读2分钟

image.png

useState里面有过一定的解释

useRef

目的

  • 如果你需要一个值,在组件不断render时保持不变
    • 初始化:const count = useRef(0)
    • 读取:count.current
  • 为什么需要current?
    • 为了保证两次useRef是同一个值(只有引用能做到)
  • 和变更数据有关的hooks

image.png

useRef知识点合集

  1. 就是相当于全局作用域,一处被修改,其他地方全更新
 const [count, setCount] = useState(0)
 const countRef = useRef(0)
useEffect(() => {
    console.log('use effect...',count)
    const timer = setInterval(() => {
        console.log('timer...count:', countRef.current)
        setCount(++countRef.current)
    }, 1000)
    return ()=> clearInterval(timer)
},[])
  1. 普遍操作,用来操作dom
const Hook =()=>{
    const [count, setCount] = useState(0)
    const btnRef = useRef(null)

    useEffect(() => {
        console.log('use effect...')
        const onClick = ()=>{
            setCount(count+1)
        }
        btnRef.current.addEventListener('click',onClick, false)
        return ()=> btnRef.current.removeEventListener('click',onClick, false)
    },[count])

    return(
        <div>
            <div>
                {count}
            </div>
            <button ref={btnRef}>click me </button>
        </div>
    )
}

记得取消绑定事件!

return ()=> btnRef.current.removeEventListener('click',onClick, false)

具体解释

const refContainer = useRef(initialValue);

useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变

如果需要一个值,在组件不断render时保持不变,那就可以使用 useRef

本质上,useRef 就像是可以在其 .current 属性中保存一个可变值的“盒子”。

我们改变的只是 refContainer.current 这个属性的值。refContainer 在每次渲染时的地址是不变的。useRef 会在每次渲染时返回同一个 ref 对象

请记住,当 ref 对象内容发生变化时,useRef 并不会通知你。变更 .current 属性不会引发组件重新渲染。只能自己手动渲染。比如在变更 .current 之后,再随便setState一个数据,这会使App再次执行。

image.png

useRef能做到有变化时自动render吗

  • 答案
    • 不能
    • 因为不符合React的理念
    • React的理念是UI=f(data)
    • 如果你想要的这个功能自己添加
    • 监听ref,当 ref.current 变化时,调用 setX 即可
  • 不想自己加就用Vue3

forwardRef

1. 函数组件无法接收ref的props

function App() {
  const buttonRef = useRef(null);
  return (
    <div className="App">
      <Button2 ref={buttonRef}>按钮</Button2>
    </div>
  );
}

const Button2 = props => {
  console.log(props);
  return <button className="red" {...props} />;
};

在App里 useRef 得到一个 buttonRef 对象,把它传给 Button2 组件,想让 buttonRef 引用到Button2对应的DOM对象。

image.png

可是控制台报错了,说函数组件不能接受refs的外部数据。打印出Button2的props,发现只有按钮被传进来了,ref没进来。外边虽然传了一个ref,但是一个函数组件找不到这个ref,读不到。

2. 使用 forwardRef传递ref

如果你的函数组件想要接受别人传来的ref参数,就必须把函数组件用 forwardRef 包起来。这样就可以接受ref作为第二个参数。不然就只有props这一个参数。forwardRef 会把别人传给你的ref帮你传进来。

const Button2=React.forwardRef((props,ref)=>{
  console.log(props);
  console.log(ref)
  return <button className="red" ref={ref}/>;
})

image.png

打印ref,由于传进来的ref参数已经被绑定到了button元素的ref属性上,所以打印出来的 ref.current 就是button这个DOM节点。

类组件是不需要的,可以直接传。