React Multi-Ref Utility
react-multi-ref is a lightweight utility library designed to simplify managing references to multiple, dynamically created React elements. It addresses a common challenge in React development where developers need to collect and interact with a collection of DOM nodes or component instances, especially when dealing with lists or forms. The current stable version is 1.0.2, indicating a mature and stable API. As a utility, its release cadence is likely slow, focusing on stability rather than frequent feature additions. Key differentiators include its simplicity, providing a `Map`-like interface to access refs by a custom key, and its explicit support for both functional components (via `useState`) and class components, with integrated TypeScript and Flow type definitions for enhanced developer experience. It avoids the complexities of managing arrays of refs manually or relying on less explicit methods.
Common errors
-
TypeError: Cannot read properties of undefined (reading 'value') or TypeError: Cannot read properties of null (reading 'value')
cause Attempting to access properties (like `.value`) on a ref that has not yet been assigned by React, or refers to an unmounted component, or the `key` used in `multiRef.ref(key)` does not correspond to an actual mounted element.fixAlways check if the ref element exists before accessing its properties: `if (inputElement) { /* access inputElement.value */ }`. Ensure the component is mounted when accessing refs, e.g., in an `useEffect` or event handler, not during render. -
React Hook 'useState' cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function.
cause Misuse of `useState` by calling it inside a non-component function or an event handler, instead of at the top level of a functional component.fixEnsure `useState(() => new MultiRef())` is called directly within the body of your functional React component, not within any nested functions or event handlers.
Warnings
- gotcha When using `react-multi-ref` in functional components, the `MultiRef` instance must be created and persisted using `useState` (e.g., `useState(() => new MultiRef())`). Do not use `useMemo` for this purpose, as React is allowed to reset memoized values at any time, which would lead to loss of ref tracking.
- gotcha Each call to `multiRef.ref(key)` requires a unique `key` parameter. This key is used to identify and retrieve the specific element from the internal `Map`. Reusing the same key for different elements or omitting the key will lead to incorrect ref mapping.
Install
-
npm install react-multi-ref -
yarn add react-multi-ref -
pnpm add react-multi-ref
Imports
- MultiRef
const MultiRef = require('react-multi-ref').MultiRef;import MultiRef from 'react-multi-ref';
- MultiRef instance
const itemRefs = useMemo(() => new MultiRef(), []);
const [itemRefs] = useState(() => new MultiRef());
- ref callback
<input ref={itemRefs.ref} /><input ref={itemRefs.ref(i)} />
Quickstart
import { useState } from 'react';
import MultiRef from 'react-multi-ref';
function MyDynamicForm() {
// Initialize MultiRef using useState to persist the instance across renders
const [inputRefs] = useState(() => new MultiRef());
// Generate 5 input fields, assigning a ref to each using its index as a key
const inputElements = new Array(5).fill(null).map((_, i) => (
<div key={i} style={{ marginBottom: '10px' }}>
<label>
Input {i + 1}:
<input type="text" ref={inputRefs.ref(i)} placeholder={`Value for item ${i + 1}`} />
</label>
</div>
));
// Event handler to collect all input values
function handleSubmit() {
const values = [];
inputRefs.map.forEach((inputElement, key) => {
if (inputElement) {
values.push(`Key ${key}: ${inputElement.value}`);
}
});
alert("Current input values:\n" + values.join('\n'));
}
return (
<div>
<h3>Dynamic Input Form</h3>
{inputElements}
<button onClick={handleSubmit} style={{ marginTop: '20px', padding: '10px 15px' }}>
Get All Input Values
</button>
</div>
);
}
// To run this example in a React application:
// export default MyDynamicForm;