How do React parent components actively "contact" child components

Keywords: React

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

Posted by kevisazombie on Mon, 22 Nov 2021 01:17:55 -0800