I'm trying to pass a ref of a component to another component. Since string refs are being deprecated I'm using callback refs.
So I have something similar to this:
<One ref={c => this.one = c}/> <Two one={this.one}/>
The problem is that whenever I try to access this.props.one
inside Two
I get undefined
.
I have even tried this on Two
:
componentDidMount(){ setTimeout(()=>{ console.log(this.props.one); },5000) }
It seems the problem is that when the prop is created, the ref doesn't exist yet since it's created once One
is mounted. But I don't know how to "refresh" the props on Two
to get the ref to the mounted component.
So what's the proper way of passing a ref to another component?
Edit
Some users have suggested to encapsulate that logic in a higher component, which in itself renders those other child components.
The problem with that approach is that you can't create reusable logic and you have to repeat the same logic over and over in those encapsulating components.
Let's say you want to create a generic <Form>
component which encapsulates the submit logic to your store, error checking, etc. And you do something like this:
<Form> <Input/> <Input/> <Input/> <Input/> <SubmitButton/> </Form>
In this example <Form>
can't access the instances (and methods) of the children since this.props.children
doesn't return those instances. It returns some list of pseudo components.
So how can you check if a certain <Input/>
has detected a validation error without passing a ref?
You have to encapsulate those components in another component with the validation logic. For example in <UserForm>
. But since each form is different the same logic has to be copied in <CategoryForm>
, <GoupForm>
, etc. This is terribly inefficient which is why I want to encapsulate the validation logic in <Form>
and pass references of the <Input>
components to <Form>
.
Use createRef to create the ref that you end up passing down. If you're passing a ref to a function component use React. forwardRef. If you're passing a ref down to a class component ensure that the prop name is anything except ref or you'll get a special prop warning.
Regular function or class components don't receive the ref argument, and ref is not available in props either. Ref forwarding is not limited to DOM components. You can forward refs to class component instances, too.
The forwardRef hooks allows React users to pass refs to child components. The ref can be created and referenced with useRef or createRef and then passed in a parent component. Using forwardRef instead of useRef is useful when a ref needs to be accessed in a parent component.
In general the "ref" feature is an anti-pattern in React. It exists to enable side-effect driven development, however in order to benefit the most from the React way of programming you should try to avoid "refs" if possible.
As for your particular issue, passing a child a ref to it's sibling is a chicken vs. egg scenario. The ref callback is fired when the child is mounted, not during render which is why your example doesn't work. One thing you can try is pushing the ref into state and then reading from state into the other child. So:
<One ref={c => !this.state.one && this.setState({ one: c })}/> <Two one={this.state.one}/>
Note: without the !this.state.one
this will cause an infinite loop.
Here is a codepen example of this working (look at the console to see the sibling ref logged): http://codepen.io/anon/pen/pbqvRA
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With