Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's a bad practice with refs in React?

I'm getting to learn React. Some guys of different sites tells everyone that using refs is a bad practice (yep, using them at all).

What's the real deal with it? Is it something bad that I will attach it to, for example, child component (so I can access inner stuff)?

Thanks!

like image 593
krzysztof Avatar asked Nov 12 '16 22:11

krzysztof


People also ask

Is using refs bad in React?

It is a general rule of thumb to avoid using refs unless you absolutely have to. The official React documentation outlined only three possible use cases where refs are entirely considered useful for lack of better alternatives: Managing focus, text selection, or media playback. Triggering imperative animations.

When Should refs be used in React?

Refs are a function provided by React to access the DOM element and the React element that you might have created on your own. They are used in cases where we want to change the value of a child component, without making use of props and all.

Is useRef bad practice?

The useRef Hook is pretty powerful and can often be misused. In general, developers should avoid using useRef if they could use useState instead. This article shows example code that uses useRef incorrectly and how to fix it. The example is a simple counter just to illustrate the issue.

What to use instead of refs 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.


2 Answers

React requires you to think the react way and refs are kind of a backdoor to the DOM that you almost never need to use. To simplify drastically, the react way of thinking is that once state changes, you re-render all the components of your UI that depend on that state. React will take care of making sure only the right bits of the DOM are updated, making the whole thing efficient and hiding the DOM from you (kinda).

For example, if your component hosts an HTMLInputElement, in React you'll wire up an event handler to track changes to the input element. Whenever the user types a character, the event handler will fire and in your handler you'll update your state with the new value of the input element. The change to the state triggers the hosting component to re-render itself, including the input element with the new value.

Here's what I mean

import React from 'react';
import ReactDOM from 'react-dom';

class Example extends React.Component {

    state = {
      inputValue: ""
    }

    handleChange = (e) => {
      this.setState({
        inputValue: e.target.value
      })
    }

    render() {
        const { inputValue } = this.state
        return ( 
          <div>
            /**.. lots of awesome ui **/
            /** an input element **/
            <input value={inputValue} onChange={this.handleChange}} />
            /** ... some more awesome ui **/
          </div>
       )
  }
}


ReactDOM.render( <Example />, document.getElementById("app") );
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>


<div id="app">
</div>

Notice how anytime the input value changes, the handler gets called, setState gets called and the componet will re-render itself in full.

Its generally bad practice to think about refs because you might get tempted to just use refs and and do things say the JQuery way, which is not the intention of the React architecture/mindset.

The best way to really understand it better is to build more React apps & components.

like image 144
skav Avatar answered Oct 17 '22 19:10

skav


Hmm... Not sure it qualifies as an answer, but it became too long-form for a comment.

Imagine you have a Dashboard that contains widgets showing various states of a system. Each widget has its own data source and its own controls. Perhaps they are even refreshed from time to time. However, when user wants to see an updated view of the system there is a "Refresh" button at Dashboard level. Implementing such a button is not trivial.

If you are in a Redux application - you'd have a choice - "faking" dispatch('refresh') for all the children. To decouple it, each widget, upon loading registers an action, so that parent simply goes through all actions and fires them, when imperative refresh is needed.

In a non Redux/Flux system, or in more complex/dynamic scenarios, this may not be possible or may not be as straightforward. It then may be better, complexity wise, to expose refresh method on all widgets and then access it from the parent (or, rather, owner):

class WidgetA extends React.Component {
    refresh() {
        console.log('WidgetA refreshed');
    }

    render() {
      return (
        <h3>WidgetA</h3>
      );
    }
}  

class WidgetB extends React.Component {
    refresh() {
        console.log('WidgetB refreshed');
    }

    render() {
      return (
        <h3>WidgetB</h3>
      );
    }
}  

class Dashboard extends React.Component {
    constructor() {
        super();

        this.onRefresh = this.handleRefresh.bind(this);
        this.onRegister = this.handleRegister.bind(this);
        this.widgets = [];
    }

    handleRegister(widget) {
        this.widgets.push(widget);
    }

    handleRefresh() {
        this.widgets.forEach((widget) => {
            widget.refresh();
        });
    }

    render() {
        return (
            <div>
                <button onClick={this.onRefresh}>Refresh</button>
                <hr />
                <WidgetA ref={this.onRegister} />
                <WidgetB ref={this.onRegister} />
            </div>
        );
    }
}

Something like that, with less verbosity, of course.

As side note, I upvoted @skav answer and think that these scenarios should be avoided. This is an exception.

CodePen Example

like image 3
ZenMaster Avatar answered Oct 17 '22 19:10

ZenMaster