In a typical React data stream, props is the only way for parent-child components to interact. To modify a subcomponent, you need to re render it with the new props. However, in some cases, you need to actively view or force modification of child components outside the typical data flow. At this time, you need to use Refs to expose DOM Refs to the parent component.
When to use Refs
Here are some situations where refs can be used:
- Manage focus, text selection or media playback;
- Trigger forced animation;
- Integrate the third-party DOM library;
- Measuring the size or position of sub DOM nodes;
Avoid using refs to do anything that can be done through declarative implementation because it breaks the encapsulation of components.
Refs and components
By default, the ref attribute must point to a DOM element or class component. You cannot use the ref attribute on function components because they have no instances. If you need to use ref in a function component, you can use forwardRef or convert the component to a class component.
React.forwardRef
React.forwardRef(props, ref)
- The second parameter ref exists only when the component is defined using React.forwardRef. Regular functions and class components do not receive ref parameters, and ref does not exist in props.
- Ref forwarding is not limited to DOM components, but can also forward refs to class component instances.
Ref forwarding
If you use React above 16.3, use ref forwarding to expose DOM Refs to the parent component. Ref forwarding enables a component to expose the refs of its subcomponents as it exposes its own refs. If you have no control over the implementation of sub components, you can only use findDOMNode(), but it has been abandoned in strict mode and is not recommended.
Implement Ref forwarding method:
- ref and forwardRef
- useImperativeHandle and forwardRef
ref and forwardRef
- The parent component creates a ref and passes it down to the child component
- The sub component obtains the ref passed to it through forwardRef
- The sub component gets the ref and forwards it down to one of its elements
- The parent component obtains the bound DOM element instance through ref.current
shortcoming
- The DOM that binds the ref element is exposed directly to the parent component
- After the parent component gets the DOM, it can perform any operation
example
The following example uses the useRef Hook introduced by React 16.6. For React 16.3, use the React.createRef() API. For earlier versions, use the refs in callback form.
// Subcomponents import React, { forwardRef, useState } from 'react' const BaseInfo = forwardRef((props, ref) => { console.log('BaseInfo --- props', props) console.log('BaseInfo --- ref', ref) const [name, setName] = useState('') const [age, setAge] = useState('') return ( <div ref={ref}> full name: <input onChange={val => setName(val)} /> Age: <input onChange={val => setAge(val)} /> </div> ) })
// Parent component import React, { useRef } from 'react' import BaseInfo from './BaseInfo' function RefTest() { const baseInfoRef = useRef(null) const getBaseInfo = () => { console.log('getBaseInfo --- baseInfoRef', baseInfoRef.current) } return ( <> <BaseInfo dataSource={{ name: 'chaos' }} ref={baseInfoRef} /> <button onClick={getBaseInfo}>Get basic information</button> </> ) }
output
After clicking the "get basic information" button:
useImperativeHandle and forwardRef
useImperativeHandle introduction
useImperativeHandle(ref, createHandle, [deps])
When using ref, customize the instance value exposed to the parent component through useImperativeHandle.
The ref passed in by the parent component and the object returned by the second parameter of useImperativeHandle are bound together through useImperativeHandle.
advantage
- Expose only the DOM methods needed by the parent component;
- In the parent component, when xxxRef.current is invoked, the object is returned.
example
// Subcomponents const OtherInfo = (props, ref) => { console.log('OtherInfo --- props', props) console.log('OtherInfo --- ref', ref) const inputRef = useRef() const [school, setSchool] = useState('') useImperativeHandle(ref, () => { console.log('useImperativeHandle --- ref', ref) return ({ focus: () => { inputRef.current.focus() }, getSchool: () => { return inputRef.current.value } }) }, [inputRef]) return ( <div> school: <input ref={inputRef} onChange={val => setSchool(val)}/> </div> ) } export default forwardRef(OtherInfo)
// Parent component import React, { useRef } from 'react' import OtherInfo from './OtherInfo' function RefTest() { const otherInfoRef = useRef(null) const getOtherInfo = () => { console.log('getOtherInfo --- otherInfoRef', otherInfoRef.current) console.log('getOtherInfo --- otherInfoRef --- getSchool', otherInfoRef.current.getSchool()) } return ( <> <OtherInfo dataSource={{ school: 'university' }} ref={otherInfoRef} /> <button onClick={getOtherInfo}>Get additional information</button> </> ) }
output