react自定义组件中使用ref
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了react自定义组件中使用ref相关的知识,希望对你有一定的参考价值。
参考技术A 自定义组件中使用ref需要用到react的2个hooks:1.forwardRef
2.useImperativeHandle
自定义组件:
使用:
先使用 React.forwardRef;再使用 connect 包一层会使 ref 属性漏掉,导致 内部实例无法传到外部;
正确的操作方式要调整高阶组件的顺序,先用connect包裹,然后再用React.forwardRef包裹。
这样就可以同时使用2个高阶组件了。
React Hooks的使用——useRefuseImperativeHandleuseLayoutEffect解析自定义Hook
一、useRef
useRef返回一个ref对象,返回的ref对象再组件的整个生命周期保持不变。
最常用的ref是两种用法:
- 用法一:引入DOM(或者组件,但是需要是class组件)元素;
案例一:引用DOM
import React, useRef from "react";
class TestCpn extends React.Component
render()
return <h2>TestCpn</h2>
function TestCpn2(props)
return <h2>TestCpn2</h2>
export default function RefHookDemo01()
const titleRef = useRef()
const inputRef = useRef()
const testRef = useRef()
const testRef2 = useRef()
function changeDOM()
titleRef.current.innerHTML = 'hello world'
inputRef.current.focus()
console.log(testRef.current)
console.log(testRef2.current)
return (
<div>
<h2 ref=titleRef>RefHookDemo01</h2>
<input type="text" ref=inputRef/>
<TestCpn ref=testRef />
<TestCpn2 ref=testRef2 />
<button onClick=e => changeDOM()>修改DOM</button>
</div>
)
- 用法二:保存一个数据,这个对象在整个生命周期中可以保存不变;
案例二:使用ref保存上一次的某一个值
import React, useEffect, useRef, useState from "react";
export default function RefHookDemo02()
const [count, setCount] = useState(0)
const numRef = useRef(count)
useEffect(() =>
numRef.current = count
, [count])
return (
<div>
/*<h2>numRef中的值: numRef.current</h2>*/
/*<h2>count中的值: count</h2>*/
<h2>count上一次的值:numRef.current</h2>
<h2>count当前的值:count</h2>
<button onClick=e => setCount(count + 10)>+10</button>
</div>
)
二、useImperativeHandle
useImperativeHandle并不是特别好理解,我们一点点来学习。
我们先来回顾一下ref和forwardRef结合使用:
- 通过forwardRef可以将ref转发到子组件;
- 子组件拿到父组件中创建的ref,绑定到自己的某一个元素中;
import React, forwardRef, useRef from "react";
const HYInput = forwardRef(
(props,ref) =>
return <input ref=ref type="text"/>
)
export default function ForwardRefDemo()
const inputRef = useRef()
return (
<div>
<HYInput ref=inputRef/>
<button onClick=e => inputRef.current.focus()>聚焦</button>
</div>
)
forwardRef的做法本身没有什么问题,但是我们是将子组件的DOM直接暴露给了父组件:
- 直接暴露给父组件带来的问题是某些情况的不可控;
- 父组件可以拿到DOM后进行任意的操作;
- 但是,事实上在上面的案例中,我们只是希望父组件可以操作的focus,其他并不希望它随意操作;
通过useImperativeHandle可以只暴露固定的操作:
- 通过useImperativeHandle的Hook,将传入的ref和useImperativeHandle第二个参数返回的对象绑定到了一起;
- 所以在父组件中,使用 inputRef.current时,实际上使用的是返回的对象;
- 比如我调用了 focus函数;
import React, forwardRef, useImperativeHandle, useRef from "react";
const HYInput = forwardRef((props, ref) =>
const inputRef = useRef()
useImperativeHandle(ref, () =>
return
focus: () =>
inputRef.current.focus()
console.log('useImperativeHandle中回调函数返回的对象里面的focus')
, [inputRef.current])
return <input ref=inputRef type="text"/>
)
export default function ForwardRefDemo02()
const inputRef = useRef()
return (
<div>
<HYInput ref=inputRef/>
<button onClick=e => inputRef.current.focus()>聚焦</button>
</div>
)
三、useLayoutEffect
useLayoutEffect看起来和useEffect非常的相似,事实上他们也只有一点区别而已:
- useEffect会在渲染的内容更新到DOM上后执行,不会阻塞DOM的更新;
- useLayoutEffect会在渲染的内容更新到DOM上之前执行,会阻塞DOM的更新;
如果我们希望在某些操作发生之后再更新DOM,那么应该将这个操作放到useLayoutEffect。
案例: useEffect和useLayoutEffect的对比
四、自定义Hook
自定义Hook本质上只是一种函数代码逻辑的抽取,严格意义上来说,它本身并不算React的特性。
需求0:所有的组件在创建和销毁时都进行打印
- 组件被创建:打印 组件被创建了;
- 组件被销毁:打印 组件被销毁了;
import React, useEffect from "react";
const Home = (props) =>
useEffect(() =>
console.log('Home组件被创建出来了~')
return () =>
console.log('Home组件被销毁了!')
, [])
return <h2>Home</h2>
const Profile = (props) =>
useEffect(() =>
console.log('Profile组件被创建出来了~')
return () =>
console.log('Profile组件被销毁了!')
, [])
return <h2>Profile</h2>
export default function CustomHookLifeDemo01()
useEffect(() =>
console.log('CustomHookLifeDemo01组件被创建出来了~')
return () =>
console.log('CustomHookLifeDemo01组件被销毁了!')
, [])
return (
<div>
<h2>CustomHookLifeDemo01</h2>
<Home />
<Profile />
</div>
)
import React, useEffect from "react";
const Home = (props) =>
useLoggingLife('Home')
return <h2>Home</h2>
const Profile = (props) =>
useLoggingLife('Profile')
return <h2>Profile</h2>
export default function CustomHookLifeDemo01()
useLoggingLife('CustomHookLifeDemo01')
return (
<div>
<h2>CustomHookLifeDemo01</h2>
<Home />
<Profile />
</div>
)
function useLoggingLife(name)
useEffect(() =>
console.log(`$name组件被创建出来了~`)
return () =>
console.log(`$name组件被销毁了!`)
, [])
需求一:Context的共享
import useContext from "react";
import TokenContext, UserContext from "../App";
function useUserContext()
const user = useContext(UserContext)
const token = useContext(TokenContext)
return [user, token]
export default useUserContext
需求二:获取鼠标滚动位置
需求三:localStorage数据存储
以上是关于react自定义组件中使用ref的主要内容,如果未能解决你的问题,请参考以下文章
React Hooks的使用——useRefuseImperativeHandleuseLayoutEffect解析自定义Hook