Dimitry Ivashchuk
Dimitry Ivashchuk

Reputation: 625

Passing ref down to child in React + Typescript

I have an app, where I need to manage focus on the inputs and for that purpose I use React ref.

I have two inputs that I want to move into a separate component. Now they look like following:

<input
  onChange={this.handleChangeOne}
  ref={refOne => (this.refOne = refOne as HTMLInputElement)}
  type="text"
/>
<input
  onChange={this.handleChangeTwo}
  ref={refTwo => (this.refTwo = refTwo as HTMLInputElement)}
  type="text"
/>

I am having trouble to make the them work in a separate component while it involves passing down ref prop. I've tried something like this:

<InputBlock

          handleChangeOne={() => this.handleChangeOne}
          handleChangeTwo={() => this.handleChangeTwo}
          refOne=(refOne => (this.refOne = refOne as HTMLInputElement)}
          refTwo={refTwo => (this.refTwo = refTwo as HTMLInputElement)}
        />

And the components itself

class InputBlock extends React.Component<IProps> {
  public render() {
    return (
      <React.Fragment>
        <input
          ref={refOne}
          onChange={handleChangeOne}
          type="text"
        />
        <input
          ref={refTwo}
          onChange={handleChangeTwo}
          type="text"
        />
      </React.Fragment>
    );
  }
}

export default InputBlock;

For the sake of example I want handleChangeOne to focus on input two, and handleChangeTwo to focus on input one

Upvotes: 5

Views: 23523

Answers (2)

AL Faiz Ahmed
AL Faiz Ahmed

Reputation: 332

For Functional Component:

import React,{useRef} from 'react';

const ParentComp = () => {
    const parentDivRef = useRef<HTMLButtonElement>(null);
    return(
        <ChildButtonComp ref={parentDivRef}>Click Me</ChildButtonComp>
    )
}
import React, { Ref, forwardRef,ReactNode } from "react";

const ChildButtonComp = forwardRef(
  (props: { children: ReactNode }, ref: Ref<HTMLButtonElement>) => {
    return <button ref={ref}>{props.children}</button>;
  }
);

Upvotes: 0

Matt McCutchen
Matt McCutchen

Reputation: 30879

I don't know what went wrong for you, but the following is working for me:

import * as React from "react";
import * as ReactDOM from "react-dom";

class OuterComponent extends React.Component<{}, {}> {
  refOne: null | HTMLInputElement = null;
  refTwo: null | HTMLInputElement = null;
  render() {
    return <InputBlock
      handleChangeOne={this.handleChangeOne}
      handleChangeTwo={this.handleChangeTwo}
      refOne={refOne => (this.refOne = refOne)}
      refTwo={refTwo => (this.refTwo = refTwo)}
    />;
  }

  handleChangeOne = () => {
    console.log("handleChangeOne", this.refOne, this.refTwo);
  }
  handleChangeTwo = () => {
    console.log("handleChangeTwo", this.refOne, this.refTwo);
  }
}

interface IProps {
  refOne: React.Ref<HTMLInputElement>;
  refTwo: React.Ref<HTMLInputElement>;
  handleChangeOne: React.ChangeEventHandler<HTMLInputElement>;
  handleChangeTwo: React.ChangeEventHandler<HTMLInputElement>;
}
class InputBlock extends React.Component<IProps> {
  public render() {
    return (
      <React.Fragment>
        <input
          ref={this.props.refOne}
          onChange={this.props.handleChangeOne}
          type="text"
        />
        <input
          ref={this.props.refTwo}
          onChange={this.props.handleChangeTwo}
          type="text"
        />
      </React.Fragment>
    );
  }
}

ReactDOM.render(<OuterComponent/>, document.getElementById("root"));

Upvotes: 9

Related Questions