使用场景
1 .获取子组件的实例,只有类组件可以使用
2 .在函数组件中定义一个全局变量,不会因为重复render重复声明,类似于组件的this.xxx
获取子组件实例
function Example(){
const inputEl=useRef(null)
function handleClick(){
console.log(inputEl)
inputEl.current.focus()
}
return (
<>
<input type="text" ref={inputEl}/>
<button onClick={handleClick}>点击</button>
</>
)
}
类组件属性
1 .我们需要保证函数组件每次render之后,某些变量不会被重复声明,比如说Dom节点,定时器id等
2 .在类组件中,可以通过给类添加一个自定义属性来保留,比如this.xx
3 .虽然可以通过useState来保留变量的值,但是useState会触发组件render,这里完全是不需要的,需要使用useRef来实现
function App () {
const [ count, setCount ] = useState(0)
const timer = useRef(null)
let timer2
useEffect(() => {
let id = setInterval(() => {
setCount(count => count + 1)
console.log(timer2)
}, 500)
console.log('123‘)
// 确实setInterval在里面跑逻辑,外面的没有改
timer.current = id
timer2 = id
return () => {
clearInterval(timer.current)
}
}, [])
const onClickRef = useCallback(() => {
clearInterval(timer.current)
}, [])
const onClick = useCallback(() => {
clearInterval(timer2)
}, [])
return (
<div>
点击次数: { count }
<button onClick={onClick}>普通</button>
<button onClick={onClickRef}>useRef</button>
</div>
)
}
export default App
//看起来不用ref也是可以实现的,难道这个bug已经修了吗??
4 .app组件每次render,都会重新声明一次timer2,好像不是吧,打印出来tmer2一直都是最初是的id..
文档
1 .useRef返回一个可变的ref对象,其.current属性被初始化为传入参数initialValue
2 .返回的ref兑现挂载组件的整个生命周期都保持不变
3 .useRef可以保存任何类型的值,类似于在class中使用this.xxx
4 .他创建的是一个普通的js对象,而useRef和自己创建一个对象的区别是,useRef会在每次渲染时返回对同一个ref对象
5 .当ref内容发生变化时,useRef是不会通知你的,变更.current属性也不会引发组件的重新渲染
forwardRef
1 .函数组件没有实例,所以函数组件无法像类组件一样接收ref属性
2 .forwardRef 可以在父组件中操作子组件的ref对象
3 .forwardRef可以将父组件中的ref对象转发到子组件的dom上
4 .子组件接收props,ref,作为参数
function Child(props,ref){
//子组件这里要把props和ref分开作为参数传进来
return (
<input type="text" ref={ref}/>
)
}
Child = React.forwardRef(Child);
//其实就是在之前的组件外面包一层,父组件使用时没有任何区别的
function Parent(){
let [number,setNumber] = useState(0);
// 在使用类组件的时候,创建 ref 返回一个对象,该对象的 current 属性值为空
// 只有当它被赋给某个元素的 ref 属性时,才会有值
// 所以父组件(类组件)创建一个 ref 对象,然后传递给子组件(类组件),子组件内部有元素使用了
// 那么父组件就可以操作子组件中的某个元素
// 但是函数组件无法接收 ref 属性 <Child ref={xxx} /> 这样是不行的
// 所以就需要用到 forwardRef 进行转发
const inputRef = useRef();//{current:''}
function getFocus(){
inputRef.current.value = 'focus';
inputRef.current.focus();
}
return (
<>
<Child ref={inputRef}/>
<button onClick={()=>setNumber({number:number+1})}>+</button>
<button onClick={getFocus}>获得焦点</button>
</>
)
}
实现类似this的效果,可以在函数内访问到最新的数据
1 .可以简单的跳出
function Example(props) {
// 把最新的 props 保存在一个 ref 中
const latestProps = useRef(props);
useEffect(() => {
latestProps.current = props;
});
useEffect(() => {
function tick() {
// 在任何时候读取最新的 props
console.log(latestProps.current);
}
const id = setInterval(tick, 1000);
return () => clearInterval(id);
}, []); // 这个 effect 从不会重新执行
}
refs补充
1 .我们利用render方法得到了一个App组件的实例,然后就可以对他做一些操作,但是在组件内,jsx是不会返回一个组件的实例的,只是一个ReactElement,只是告诉React被挂在的组件应该长什么样子,
2 .ref的意思就是组件被调用的时候会新建一个改组件的实例,然后ref指向这个实例
3 .可以通过ref获得DOM节点.人如果把refs放到React组件,就会获得组件的实例.这样可以调用组件的实例方法
4 .组件的ref可以通过findDomNode来返回生成的DOM
5 .为了防止内存泄露,所有组件卸载的时候都会把ref置为null
useRef在函数组件中的使用
1 .之前class的写法是可以随时绑定在class上,但是function的操作是不能简单绑定上去的.需要操作
app.js里面
const contentRef=useRef(null)
<Content ref={contentRef}>
content.js
import React,{useImperativeHandle} from 'react'
function Content(props,ref){
function resetCount(){
//做一些事情
}
useImperativeHandle(ref,()=>{
resetCount,resetCount,
//一个是传递到外面的函数名,一个是里面的函数u
})
}
export default React.ForwardRef(Count)
2 .useImperativeHandle中定义了resetCount,以及使用React.forward获取ref,在app中为content中定义ref属性,然后就可以在外部父组件中使用通过ref调用子组件的resetCount方法
3 .函数式组件的Ref是什么?就是里面暴露出来的东西
4 .绑定到HTML元素上,对应的DOM元素
5 .绑定在class组件,获取的class组件实例
React.createRef,useRef
1 .两者都是创建了一个包含current属性的对象,绑定ref时,对应的属性和函数都在current对应的对象中
2 .createRef创建的时React.RefObject类型.只能读
3 .useRef创建的是React.MutableRefObject,是可读写的.可以保存任何可变的值,使用方式类似于class组件的this实例变量
4 .对函数式组件可设置ref,且设置的ref是一个可变对象,存放组件的变量,也能通过useImperativeHandle访问函数式组件的方法.但是不能像ref设置到class组件和DOM元素上那样可以获取到实例