在之前的React的类组件ref使用
笔记中,介绍了在类组件中使用ref,本笔记主要记录在函数组件中使用ref,在函数组件中是不能直接使用ref的,而是要使用Hook函数useRef
来实现类似在类组件中ref的功能。
初识useRef
useRef
是 React 提供的一个 Hook 函数,它返回的是一个ref对象,返回的ref对象在组件的整个生命周期的栈
中保存的值是不变的。它返回的对象包含一个可变的 current
属性,可以在函数式组件中使用它来保存任何可变值,包括 DOM 元素或组件实例的引用。useRef
的主要作用是保存一些变量,而这些变量的值改变时不会触发组件的重渲染。
useRef
的格式:
const refObj = useRef(initialValue);
initialValue
是ref对象的初始值,可以是任何 JavaScript 类型的值,也可以不传初始值。
useRef
的使用:
console.log(refObj.current)
在返回的ref对象,可以通过.current
可以获取保存在useRef
的值,当没有传入初始值时,通过.current
获取的值为undefined
。
获取DOM元素
const Parent = () => {
const refObj = useRef()
const handleClick = () =>{
console.log(refObj.current)
// 获取input的值
console.log(refObj.current.value)
}
return (
<div>
<h2>React的Hooks函数useRef和useImperativeHandlet</h2>
<input type="text" ref={refObj}/>
<button onClick={handleClick}>click me</button>
</div>
);
};
运行上面代码,就可以获取到input的DOM操作,而useRef
除了可以获取DOM外 ,也可以当作一个变量使用,当变量的current
值改变时,并不会重新执行函数。
React.creatRef()和useref区别
let prev1,prev2;
const Parent = () => {
console.log('执行函数了')
const [count, setCount] = useState(0)
const refCount1 = useRef(0)
const refCount2 = React.createRef(0)
if(!prev1){
prev1 = refCount1
prev2 = refCount2
} else {
console.log(prev1 === refCount1)// true
console.log(prev2 === refCount2) // false
}
const handleClick = () =>{
setCount(count + 1)
}
return (
<div>
<h2>React的Hooks函数useRef和useImperativeHandlet</h2>
<p>count:{count}</p>
<button onClick={handleClick}>click me</button>
</div>
);
};
运行上面代码,可以得知:
useRef
在每次组件更新的时候,再次执行useRef
方法的时候,不会创建新的ref对象,获取到的还是第一次创建的那个ref对象
creatRef
在每次组件更新的时候,都会创建一个新的ref对象出来
组件嵌套
通过forwardRef
可以将ref转发到子组件,子组件拿到父组件中创建的ref,绑定到自己的某一个元素中,父组件中就可以获取到子组件中绑定了ref的元素。
const Child = forwardRef((props,ref)=>{
return (
<div>
我是子组件
<input type="text" ref={ref}/>
</div>
)
})
const Parent = () => {
const refDom = useRef()
const handleClickChild = () =>{
console.log(refDom)
// 获取input的值
console.log(refDom.current.value)
}
return (
<div>
<h2>React的Hooks函数useRef和useImperativeHandlet</h2>
<Child ref={refDom}></Child>
<button onClick={handleClickChild}>click child</button>
</div>
);
};
上面直接使用forwardRef
获取子组件的DOM,并不安全,因为这样子组件的DOM直接暴露给了父组件。如果直接暴露给父组件,父组件可以拿到DOM后可以进行任意的操作。上面的代码只是希望父组件可以拿到子组件的value值就行了,其他并不希望它操作,比如:修改元素内容的操作refDom.current.value = "222"
等,所以要限制父组件直接操作子组件的DOM。因此要使用useImperativeHandle
函数,那它的作用是啥呢?
useImperativeHandle
useImperativeHandle
函数有两个参数:
- 参数一: 传入
forwardRef
引用父组件的ref对象 - 参数二: 传入一个回调函数, 返回一个对象,对象里面存储需要暴露给父组件的属性或方法
通过useImperativeHandle
,在父组件中可以使用·xxx.current
拿到子组件暴露的属性和方法。
const Child = forwardRef((props,ref)=>{
const [val, setVal] = useState(0)
const refInput = useRef()
const handleClick = () =>{
setVal(val + 1)
}
const getInput = ()=> {
return refInput.current
}
// 暴露组件的属性和方法
useImperativeHandle(ref, ()=>{
return {
val,
handleClick,
getInput
}
})
return (
<div>
我是子组件
<p>Val:{val}</p>
<input type="text" ref={refInput}/>
</div>
)
})
const Parent = () => {
const refDom = useRef()
const handleClickChild = () =>{
console.log(refDom)
// 获取input的值
console.log(refDom.current.value)
}
//改变子组件的val值
const handleChangeChildVal = ()=> {
refDom.current.handleClick()
}
// 获取子组件input
const handleClickGetChild = () =>{
console.log(refDom.current.getInput().value)
}
return (
<div>
<h2>React的Hooks函数useRef和useImperativeHandlet</h2>
<Child ref={refDom}></Child>
<button onClick={handleChangeChildVal}>change val</button>
<button onClick={handleClickChild}>click child</button>
<button onClick={handleClickGetChild}>child input</button>
</div>
);
};
以上如果还想获取子组件内的input的值,就只有重新在子组件创建ref对象,然后暴露出来。这样以来想要在外部使用什么功能完全由内部的子组件决定。