Introduction
In traditional web development, accessing and manipulating the Document Object Model (DOM) is straightforward with methods like document.getElementById()
. However, when working with React, a popular JavaScript library for building user interfaces, direct manipulation of the DOM using these methods is not recommended. Instead, React provides a more declarative approach to interacting with the DOM through components and state management.
This tutorial will guide you through accessing DOM elements in React by leveraging refs, exploring different techniques depending on whether you are working with class or functional components. We’ll also discuss why direct DOM manipulation should be minimized in React applications.
Understanding Refs
Refs provide a way to access DOM nodes directly within your React component without using document.getElementById()
or similar methods. This is essential when integrating non-React code that requires direct interaction with the DOM or when you need to perform actions like focusing on an input field, measuring element dimensions, etc.
Creating Refs in Class Components
In class components, refs are typically created using either callback functions or React.createRef()
. Here’s how you can use both methods:
Callback Refs
Callback refs involve passing a function as the ref
attribute. This function receives the DOM node as its argument, allowing you to store it in an instance variable.
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.progressBarRefs = {};
}
addPercent = (value) => {
const progressBar = this.progressBarRefs[this.state.baction];
if (progressBar) {
progressBar.addPrecent(value);
}
};
render() {
return (
<div>
<ProgressBar completed={25} ref={(el) => { this.progressBarRefs['Progress1'] = el; }} />
<button onClick={() => this.setState({ baction: 'Progress1' })}>Select Progress1</button>
<button onClick={() => this.addPercent(10)}>+10%</button>
</div>
);
}
}
React.createRef()
React.createRef()
is a more modern approach available from React 16.3 onwards. This method allows you to create refs in the constructor and attach them directly.
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.progress1Ref = React.createRef();
}
addPercent = (value) => {
if (this.progress1Ref.current) {
this.progress1Ref.current.addPrecent(value);
}
};
render() {
return (
<div>
<ProgressBar completed={25} ref={this.progress1Ref} />
<button onClick={() => this.addPercent(10)}>+10%</button>
</div>
);
}
}
Using Refs in Functional Components
With the introduction of hooks in React 16.8, functional components can also access DOM elements using refs through useRef
and React.forwardRef
.
useRef
Hook
The useRef
hook is used to create a mutable reference object that persists for the lifetime of the component.
import React, { useRef } from 'react';
const App = () => {
const progress1Ref = useRef();
const addPercent = (value) => {
if (progress1Ref.current) {
progress1Ref.current.addPrecent(value);
}
};
return (
<div>
<ProgressBar completed={25} ref={progress1Ref} />
<button onClick={() => addPercent(10)}>+10%</button>
</div>
);
};
React.forwardRef
To pass refs through functional components to child DOM elements or other components, use React.forwardRef
.
import React from 'react';
const ProgressBar = React.forwardRef((props, ref) => {
return <div ref={ref} style={{ width: `${props.completed}%` }} />;
});
const App = () => {
const progress1Ref = useRef();
const addPercent = (value) => {
if (progress1Ref.current) {
console.log('Progress:', value); // Replace with actual logic
}
};
return (
<div>
<ProgressBar completed={25} ref={progress1Ref} />
<button onClick={() => addPercent(10)}>+10%</button>
</div>
);
};
Why Avoid Direct DOM Manipulation?
React is designed to manage the state of your application and update the UI in response to changes. By directly manipulating the DOM, you bypass React’s virtual DOM reconciliation process, which can lead to unpredictable behavior and bugs.
Instead of using direct DOM access:
- Leverage React’s declarative nature by managing component states.
- Utilize refs sparingly for scenarios where direct interaction is necessary.
- Consider alternative patterns like lifting state up or context API when dealing with shared state across components.
Conclusion
Accessing DOM elements in React should be done thoughtfully, using the tools provided by React such as refs. While refs can be a powerful tool for interacting with the DOM directly within your components, they should be used sparingly to maintain the declarative and component-based nature of React applications. By understanding when and how to use refs, you’ll be able to build more robust and maintainable React applications.