我们在编写react的时候,很多情况下,都是操作状态、方法,然后通过hook或者render 让视图重新更新,进而达到预期展示效果。我们将这个叫做 受控组件。但是在一些特定情况下,比如父组件操控子组件内部属性方法、组件获取某个DOM,进而操作DOM,来实现需求和效果。我们叫这个为 非受控组件。当我们获取DOM 或者组件实例的时候,会用到 REF 这个属性。
获取DOM元素
1. 给元素直接设置字符串方式
-
给需要获取的元素设置 ref="strName" js中通过
this.refs.strName来获取相应的的DOM元素 -
这种方式只适用于 类组件中获取 DOM元素。
-
该方式 慢慢会被取缔,所以不建议使用。
class RefSty extend React.component { render() { return( <div ref="refStr">ref字符串使用方式</div> ) } componentDidMount() { log(this.refs.refStr); } }
2. 给元素ref属性 设置 function
-
直接在元素上绑定 ref={domPar=>this.refFn=domPar},
- 如果是类组件,在js 中,使用
this.refs.refFn来获取DOM元素 【domPar是函数的形参,存储当前的DOM值】 - 如果是函数组件,在js中,直接获取对应的变量值就好。注意函数组件没有this一说,所以 {domPar=>refFn=domPar} 这样来处理
/// 类组件 class RefSty extend React.component { render() { return( <div ref={domPar=>this.refFn=domPar}>ref函数式使用方式</div> ) } componentDidMount() { log(this.refFn); } } /// 函数组件 export default function RefStateFn(props) { let refFn; useEffect(()=>{ console.log(refFn); }) return ( <div className='demo'> <p ref={x=>refFn=x}>ref函数式使用方式</p> </div> ) } - 如果是类组件,在js 中,使用
-
函数组件使用 ref的函数方法,由于每次更新,函数组件都会重新渲染,然后ref会重新执行,创建新的上下文、变量这些内容,所以在函数组件中,并不推荐使用
3. 使用creatRef方式获取DOM
-
给状态绑定
curRef=React.createEef()方法,默认返回{current: null}。- 类组件中:在DOM结构上直接使用
ref={this.curRef}, js中可以通过this.curRef.current来获取到DOM - 静态组件中:DOM结构使用
ref={curRef},js中使用 curRef.current 来操作。
/// 类组件 class RefSty extend React.component { curRef=React.createEef(); render() { return( <div ref={curRef}>ref的createRef使用方式</div> ) } componentDidMount() { log(this.curRef.current); } } /// 函数组件 export default function RefStateFn(props) { let curRef=React.createRef(); useEffect(()=>{ console.log(curRef.current); },[]) return ( <div className='demo'> <p ref={curRef}>ref函数式使用方式</p> </div> ) } - 类组件中:在DOM结构上直接使用
4. 使用 useRef 获取 DOM
-
useRef 属于 hook 函数,所以只能在函数组件中使用。禁止在类组件中使用
export default function UseStateFn(props) { let box = useRef(null); useEffect(()=>{ console.log(box.current) },[]); return ( <div className='demo' ref={box}> ref的hook函数useRef使用方式 </div> ) }- 第一步: 创建 useRef 对象
let box = useRef(null) => {current: null} - 第二步: 给DOM元素绑定 ref
ref={box} - 第三步: 在创建出DOM后,使用
box.current直接获取
- 第一步: 创建 useRef 对象
5. useRef 和 createRef 的比较
useRef和React.createRef, 两者在 初次创建的时候,都可以获取到DOM值。useRef属于hook函数,所以只能在函数组件中使用useRef在更新的时候,不会再重新创建 REF 对象,直接使用的是 初次创建的 DOM对象React.createRef是react提供的一个方法,可以在类组件和 函数组件中使用React.createRef在函数中使用,更新时候,函数会重新执行,然后通过 createRef 创建的Ref对象,会重新创建的。所以在 函数组件中,不建议使用 createRef 方法。
ref 绑定 类组件
- 我们通过 REF 获取到类组件信息,都是 类组件的实例信息,我们可以通过实例信息去获取其内部的 状态、方法等。
1. 使用基本方式 绑定
- 在父组件插入子组件[类]时候,可以直接在 子组件中使用。
class Child extend React.Component{ state = {x: 123} render(){ return ( <div> 子组件 </div> ) } } class Parent extend React.Component{ componentDidMount() { log(this.childComp) } render() { return <> <Child ref={refPar => this.childComp = refPar} /> </> } }
2. hook 绑定
- hook函数只能用于函数组件中,所以当父组件是函数组件、子组件属于类组件时候可以使用
/// 父组件 函数组件 export default function UseStateFn(props) { let box = useRef(null); useEffect(() => { console.log(box, 'box') }, []); return ( <> <Child ref={box} /> </> ) } /// 子组件 类组件 class Child extends React.Component { state = { x: 123, } render() { return ( <div> <p>childX: {this.state.x}</p> </div> ) } }
ref绑定子函数组件
1. ref + React.forwardRef();
-
给函数组件直接设置 ref,是会报错的。
Function components cannot be given refs.Attempts to access thisref will fail.- 我们需要通过 React.forWardRef 来实现转发,获取到组件内部的某个元素
const Child =React.forwardRef(function Child(_props, ref){ return <> <button ref={ref}>按钮</button> </> }) class Parent extends React.Component { render() { return ( <div> <Child ref={params => this.child = params}/> </div> ) } componentDidMount() { log(this.child) } } function Parent1() { let child = React.createRef(); useEffect(()=>{ log(child.current) }) return <> <Child ref={child} /> </> }2. 使用hook 获取函数组件
- 需要借助 React.forward, 获取到的是 函数组件某个DOM元素的信息
/// parent export default function UseStateFn(props) { let box = useRef(null); useEffect(() => { console.log(box, 'box') }, []); return ( <div className='demo'> <ChildFn ref={box} /> </div> ) } /// child const ChildFn = React.forwardRef(function Child(props, refs) { console.log(refs, 'childFn'); return ( <div> <p ref={refs}>这是一个函数的子组件</p> </div> ) })
3. 想获取函数组件内部的状态、方法
-
需要 配合
useImperativeHandle来一起使用。 -
useImperativeHandle(ref,()=>{ return{} })/// parent function Parent() { let child = useRef(null); useEffect(()=>{ log(child.current) }) return ( <Child ref={child}/> ) } /// child const Child = React.forwardRef(function Child(props, ref){ let [state, setState] = useState(10); const handle = ()=> {}; useImperativeHandle(ref,()=>{ return { state, handle } }) return <p> 子组件信息{state} </p> })
-
目前想到的ref相关内容是这么多。总结:
- ref 可以是字符串,函数,变量值。
- ref 可以存储DOM元素和组件的实例
- 在处理函数组件的时候,ref 需要和 React.forwardRef 一起使用。
- 在类组件中,我们获取的是类组件的实例,可以直接拿到对应的状态、方法等所有信息。
- 在函数组件中,正常情况下,通过参数传递进入,然后通过ref绑定,拿到某个元素
- 为了让函数组件也可以获取到 属性方法,hook函数中提供了一个
React.useImperativeHandle方法,第二个参数的返回值,存储的就是可以获取到的 状态、方法。