قالب وردپرس درنا توس
Home / Tips and Tricks / Using Refs in React – CloudSavvy IT

Using Refs in React – CloudSavvy IT



Reply logo on a dark background

Refs are a React function that gives you direct access to DOM nodes created by a component render() method. They provide a way to break React’s declarative view so that you can call browser APIs.

When working with React, you usually specify what a component’s DOM should look like with JSX:

class MyComponent extends React.Component {
 
    state = {value: ""};
 
    handleChange = e => this.setState({value: e.target.value});
 
    render() {
        return (
            <div>
                <h1>Hello World!h1>
                <input
                    onChange={this.handleChange}
                    value={this.state.value} />
            div>
        );
    }
 
};

Internally, React sweats the JSX to figure out how to manipulate the DOM. It uses browser functions such as document.createElement() and document.appendChild() to create the DOM structure you declared.

This drastically simplifies the code you need to programmatically update the DOM. To change elements, change their props or update the status of a component. React then calculates the differences and makes the DOM adjustments necessary.

The case for refs

Normally, you cannot access the DOM nodes created by React. In the example above, we have no control over the elements we created. This becomes problematic if you want to call a DOM API that cannot be declaratively accessed through React props.

Let’s see what happens if the user enters an invalid value in the text input. If they then clicked Submit in a real form, it would be a good idea to show an error message and put the focus back into the text entry. You cannot do this without accessing the entrance’s DOM node. You have to focus() method on that element.

Enter refs. Refs give you a first-class way to “reference” React’s DOM nodes. You can solve the focus problem by assigning a ref to the input. Refs are objects with a current property containing the DOM node they point to.

A ref

Refs are usually made explicit by calling React.createRef()You then assign them to a component instance using the special ref support. This is not a real plug and cannot be accessed by the component.

class DemoComponent extends React.Component {
 
    inputRef = React.createRef();
 
    focusInput = () => this.inputRef?.current.focus();
 
    render() {
        return (
            <div>
                <input ref={this.inputRef} />
                <button onClick={this.focusInput} />
            div>
        );
    }
 
}

The ref is assigned to the instance property inputRefThis is then handed over to the input element through ref support. When the button is clicked, it becomes focusInput() method is called. This gives access to it current property of the ref, which contains the actual DOM node of the input. It can call now focus() to focus the text field.

The current may be owned by refs nullThis happens when the ref is not assigned to a rendered DOM element. In this example inputRef.current will be null till the render() method is called and the input picks up the ref. For this reason, the optional chain operator (?.) is used in focusInput() to gracefully deal with the scenario where the referee is not yet referring to anything.

Assigning Refs to Responding Components

The example above shows how refs work when used with regular HTML elements. You can also assign a ref to React component instances. Allows you to call methods defined by children you render.

class View extends React.Component {
 
    state = {
        error: true;    // Example!
    };
 
    formRef = React.createRef();
 
    focusForm = () => this.formRef?.current.focusInput();
 
    submit = () => {
        if (this.state.error) {
            alert("There was an error; check your input.");
            this.focusForm();
        }
    };
 
    render() {
        return (
            <div>
                <Form ref={this.formRef} />
                <Button onClick={this.submit} />
            div>
        );
    }
 
}
 
class Form extends React.Component {
 
    inputRef = React.createRef();
 
    focusInput() {
        this.inputRef.current?.focus();
    }
 
    render() {
        return <input ref={this.inputRef} />
    }
}

In this scenario it is current owned by the formRef in View does not refer to a DOM node. Instead, it refers to it Form component instance that was displayed.

You have to be careful when using this approach. Always pass data to child components via props, rather than using refs as a callback system.

In general, a ref should be used when direct DOM interaction is unavoidable. This remains true when assigning a ref to a React component instance. You may not call arbitrary component methods through a ref.

Our example meets this requirement – Form is a presentation component, while View is a complex container that can display multiple forms. It must be able to focus on problematic fields, even if it does not directly reflect them. The solution is to use a component reference carefully so that its use is justified by the need to manipulate the DOM.

Refs and functional components

Functional components cannot receive refs. They don’t have instances so there’s nothing to assign the ref to. But you can use forward to redirect a reference to a DOM clause.

Ref forwarding is an optional function that allows a component to pass a ref it receives to one of the children it displays. To forward a ref, wrap your component with a call to React’s forwardRef() function:

const InputComponent = React.forwardRef((props, ref) => (
    <input ref={ref} value={props.value} />
));

forwardRef() accepts a function that should return a React clause. The function is called when it needs to render and takes two parameters: the props and the forwarded ref.

Using Refs within functional components

While functional components cannot receive refs directly, they can create them with the useRef() hook. This is equal to the createRef() method available in class components.

const InputComponent = props => {
    const ref = useRef();
    return <input ref={ref} value={props.value} />
};

Most importantly, useRef() can be used for more than just refs. Its real role is to provide a way to keep data between calls to a functional component. It returns an object with a current property that React then maintains and fixes each time the component is displayed.

So you can use useRef() to preserve arbitrary data in functional components. Set the current property of the returned object to the value you want to keep.

Callback Refs

A final way to work with refs is through the “callback” pattern. With this approach you don’t have to make a manual call createRef() or useRef()Instead, set the ref prop to a function that will call React while rendering. It passes the element’s DOM node as the only argument to the function.

class CallbackRef extends React.Component {
    render() {
        return <input ref={el => this.inputRef = el} />
    }
}

This is a more concise way of creating refs assigned to instance properties. Your function receives the DOM node directly – there is none .current to handle, unlike createRef()

Conclusion

With React Refs you can handle scenarios that you cannot solve with declarative view alone. They are your way to direct DOM manipulation when working with forms, media playback and animations. You will also find yourself looking for references if you need to integrate a third-party JavaScript library not built for React.

While referees are flexible, you shouldn’t overuse them. Before creating a reference, check your components to make sure you cannot reach your goal declaratively.

Refs are a necessary part of React, but they contradict the principles of the library. Too many referees can quickly make your app difficult to maintain. They let you break the top-down data flow that props usually enforce.


Source link