Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React: Losing ref values

Tags:

reactjs

refs

I am using two components and I am using this pattern: child component should stay isolated as much it can - it is handling its own validation error. Parent component should check for errors which have dependencies between children. So, in my case: password field and password confirmation field.

Here is my code:

a) SignUp (parent)

Setting initial state.

 constructor() {
     super();

     this.state = {
         isPasswordMatching: false
     };
 }

In render() method I am outputting my child component. Through prop called callback I am propagating method isPasswordMatching() by binding parent's this. The goal is that method can be called within child component.

<TextInput
    id={'password'}
    ref={(ref) => this.password = ref}
    callback={this.isPasswordMatching.bind(this)}
    // some other unimportant props
/>

<TextInput
    id={'passwordConfirm'}
    ref={(ref) => this.passwordConfirm = ref}
    ...

isPasswordMatching() method is checking if passwords match (through refs this.password and this.passwordConfirm) and then updates state. Please note that this method is called inside child (password or passwordConfirm).

isPasswordMatching() {
    this.setState({
        isPasswordMatching: this.password.state.value === this.passwordConfirm.state.value
    });
}

b) TextInput (child)

Setting initial state.

constructor() {
    super();

    this.state = {
        value: '',
        isValid: false
    };
}

On blur validation is done and state is updated.

onBlur(event) {

    // doing validation and preparing error messages

    this.setState({
        value: value,
        isValid: this.error === null
    });
}

Latest. Callback prop is called.

componentDidUpdate(prevProps) {
    if (prevProps.id === 'password' || prevProps.id === 'passwordConfirm') {
        prevProps.callback();
    }
}

Issue

Somehow my refs are lost. Scenario:

  1. Parent component is renderder
  2. Child components are rendered
  3. I am entering one of input fields and get out (this invokes onBlur() method) - state gets updated, child component is rendered
  4. componentDidUpdate() is invoked and prevProp.callback() as well
  5. When going to isPasswordMatching() method I am outputting this.password and this.passwordConfirm - they are objects with expected values of reference. Updating state of parent - component gets rendered.
  6. Then again all children are rendered, components get updated, callback is called but this time this.password and this.passwordConfirm are null.

I have no idea why references are kinda lost. Should I be doing something differently? Thank you in advance.

like image 302
be-codified Avatar asked Jul 15 '16 17:07

be-codified


People also ask

Why is ref always null?

A React ref most commonly returns undefined or null when we try to access its current property before its corresponding DOM element is rendered. To get around this, access the ref in the useEffect hook or when an event is triggered.

What can I use instead of ref IN React?

In react, there is another way to use refs that is called "callback refs" and it gives more control when the refs are set and unset. Instead of creating refs by createRef() method, React allows a way to create refs by passing a callback function to the ref attribute of a component.

Is ref deprecated in React?

[React Native]Refs - Using string literals in ref attributes is deprecated (react/no-string-refs) If you worked with React before, you might be familiar with an older API where the ref attribute is a string, like "textInput" , and the DOM node is accessed as this. refs.

How do you clean a refs React?

Just save the current ref value to a locally scoped variable to be closed over in the effect's cleanup function. You will want to cleanup any old subscribed observers, refs, callbacks, etc... when, if ever, the callbackFunction value updates. Add it to the dependency array.


2 Answers

See the react documentation here, with important warnings and advise about when to use or not to use refs.

Note that when the referenced component is unmounted and whenever the ref changes, the old ref will be called with null as an argument. This prevents memory leaks in the case that the instance is stored, as in the second example. Also note that when writing refs with inline function expressions as in the examples here, React sees a different function object each time so on every update, ref will be called with null immediately before it's called with the component instance.

like image 58
Galeel Bhasha Avatar answered Sep 17 '22 11:09

Galeel Bhasha


I'm not sure if this answers @be-codified's question for not, but I found this running into a similar issue. In my case, it turned out that it was due to using a functional component.

https://reactjs.org/docs/refs-and-the-dom.html#refs-and-functional-components

Refs and Functional Components You may not use the ref attribute on functional components because they don’t You should convert the component to a class if you need a ref to it, just like you do when you need lifecycle methods or state.
You can, however, use the ref attribute inside a functional component as long as you refer to a DOM element or a class component

The documentation explains what you should do to solve the issue if you have control of the component you're trying to render.

However in my case, the component was from a 3rd party library. So, simply wrapping the component worked fine.

Working

<div ref={element => this.elementName = element}>
    <FunctionalComponent />
</div>

Not Working
sets this.elementName to null

<FunctionalComponent ref={element => this.elementName = element} />

Hope this helps anyone finding this question like I did.

like image 45
mawburn Avatar answered Sep 17 '22 11:09

mawburn