Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React - use ref in component AND pass it to parent in props

Update: my problem was actually due to typos – the general approach works fine if you'd like to use a child element ref in both a child and parent component.

Here's a working example of the approach that works: https://codesandbox.io/s/rwj7z7o7oo


Original post:

I'm trying to forward a ref to the parent component, while also making the ref accessible for functions in the child (which is a class). Currently, I can successfully pass the ref to the parent, but the ref is no longer accessible in the child.

class Child extends React.Component {
    // Unable to access the forwarded ref here:
    componentDidMount() {
        console.log(this.props.forwardedRef); // null
    }

    render() {
        return <input type="text" ref={this.props.forwardedRef} />
    }
}

// Parent is able to access the ref:
const Parent = () => {
    const childRef = useRef(null);

    function handleClick() {
        console.log(childRef.current); // correctly ref's the input el
    }

    return (
        <Child forwardedRef={childRef} onClick={handleClick} />
    );
}

Is there another approach will let me use the ref in both Child and Parent?

like image 718
Stephen Avatar asked Nov 22 '18 05:11

Stephen


People also ask

Can you pass a ref as a prop React?

There is one caveat to the above example: refs will not get passed through. That's because ref is not a prop. Like key , it's handled differently by React. If you add a ref to a HOC, the ref will refer to the outermost container component, not the wrapped component.

Can we use ref on component?

We should not use ref attribute on function components because they do not have instances. React will assign the current property with Dom element when component mount and assign null to it when component unmount. ref updates happen before componentDidMount or componentDidUpdate methods.

How do I access refs of a child component in the parent component?

Accessing Refs When we assign a ref to an element or child component in the render, then we can access the element using the current attribute of the ref. const element = this. myRef.


2 Answers

useRef returns values that are like instance variables classes. In your case, there is nothing that would cause the component to render even if your set the ref and hence componentDidUpdate of child wouldn't run.

Also you haven't returned anything from Child component.

class Child extends React.Component {
  // Unable to access the forwarded ref here:
  componentDidUpdate(prevProps) {
    console.log(this.props.forwardedRef); // null
    console.log(prevProps.forwardedRef); // null
  }

  render() {
    return (
      <React.Fragment>
        <input type="text" ref={this.props.forwardedRef} />
        <div>{this.props.count}</div>
        <input type="button" onClick={this.props.onClick} value={"Click"} />
      </React.Fragment>
    );
  }
}

// Parent is able to access the ref:
const Parent = () => {
  const childRef = useRef(null);
  const [count, setCount] = useState(0);

  function handleClick() {
    console.log(childRef.current); // correctly ref's the input el
    setCount(count => count + 1);
  }

  return <Child forwardedRef={childRef} count={count} onClick={handleClick} />;
};

Working demo

like image 155
Shubham Khatri Avatar answered Sep 24 '22 08:09

Shubham Khatri


React Hooks

No need to pass forwardedRef in React hooks.

You can access ref variable directly in forwardRef function:

const Child = React.forwardRef((_props, ref) => {
  React.useLayoutEffect(() => {
    if (ref.current) {
      ref.current.insertAdjacentHTML(
        "beforeend",
        "<span>Ref works in child</span>"
      );
    }
  }, [ref]);

  return (
    <div className="child" ref={ref}>
      <h2>Child Component</h2>
    </div>
  );
});

const Parent = () => {
  const childRef = React.useRef(null);

  React.useLayoutEffect(() => {
    if (childRef.current) {
      childRef.current.insertAdjacentHTML(
        "afterbegin",
        "<span>Ref works in parent</span>"
      );
    }
  }, []);

  return (
    <div className="parent">
      <h1>Parent Component</h1>
      <Child ref={childRef} />
    </div>
  );
};

function App() {
  return (
    <div className="App">
      <Parent />
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(
  <App />,
  rootElement
);
.App {
  font-family: sans-serif;
  text-align: center;
  padding: 1rem;
}

.parent {
  border: 1px dashed red;
  padding: 1rem;
}

.child {
  border: 1px dashed blue;
  padding: 1rem;
}

span {
  display: block;
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
  margin: 0.5rem;
  color: #777;
  border: 1px dashed #999;
}
<div id="root"></div>
<script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
  <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>
like image 32
glinda93 Avatar answered Sep 24 '22 08:09

glinda93