I'm encountering an issue with React refs that's a bit perplexing. I have a higher-order component (HOC) that dynamically generates child components based on a prop, and I'm trying to use refs to access those child components. However, it seems like the refs aren't being properly set, and I can't figure out why.
Here's a simplified example of my code:
import React, { useRef, useEffect } from 'react';
const DynamicComponent = ({ name }) => {
// Some component logic here...
return <div>{name}</div>;
};
const HOC = ({ dynamicComponentProps }) => {
const dynamicRef = useRef();
useEffect(() => {
console.log(dynamicRef.current); // This logs 'null'
// Some other logic with dynamicRef.current...
}, [dynamicRef.current]);
const renderDynamicComponent = () => {
return dynamicComponentProps.map((props, index) => (
<DynamicComponent ref={dynamicRef} key={index} {...props} />
));
};
return <div>{renderDynamicComponent()}</div>;
};
const App = () => {
const dynamicComponentProps = [
{ name: 'Component A' },
{ name: 'Component B' },
{ name: 'Component C' },
];
return <HOC dynamicComponentProps={dynamicComponentProps} />;
};
In the useEffect
block inside the HOC
, the dynamicRef.current
is always null
, even though the components are rendered. Am I missing something about refs in dynamically generated components, or is there a better approach to achieve what I'm trying to do?
What I Tried:
I attempted to use React refs in a higher-order component (HOC) that dynamically generates child components based on a prop. I created a ref (dynamicRef
) within the HOC and assigned it to each dynamically generated child component.
What I Expected:
I expected that when I access dynamicRef.current
in the useEffect
block, it would point to the last dynamically generated child component, allowing me to perform actions or access properties of that component.
What Actually Resulted:
Surprisingly, when I logged dynamicRef.current
, it always showed null
, indicating that the refs weren't being properly set. Despite the components being rendered, the ref wasn't pointing to any of them.
The problem is that you are trying to assign the same ref for all your components. a ref needs to be assined to a single instance of a component so we can modify your code to the following
import React, { useRef, useEffect } from "react";
const DynamicComponent = React.forwardRef(({ name }, ref) => {
// Some component logic here...
return <div ref={ref}>{name}</div>;
});
const HOC = ({ dynamicComponentProps }) => {
const dynamicRefs = dynamicComponentProps.map(() => useRef(null));
useEffect(() => {
dynamicRefs.forEach((ref) => {
console.log(ref.current); // This should log the div element
// Some other logic with ref.current...
});
}, [dynamicRefs]);
const renderDynamicComponent = () => {
return dynamicComponentProps.map((props, index) => (
<DynamicComponent ref={dynamicRefs[index]} key={index} {...props} />
));
};
return <div>{renderDynamicComponent()}</div>;
};
const App = () => {
const dynamicComponentProps = [
{ name: "Component A" },
{ name: "Component B" },
{ name: "Component C" },
];
return <HOC dynamicComponentProps={dynamicComponentProps} />;
};
export default App;
in this updated version we have a seperate ref foreach component. to make this work we add to modfiy a few other things as well
EDIT
As pointed out in the comments the above code does break react rule of calling hook in side a loop so we can change the code a little instead of having an array of refs we now have a ref containings an array
import React, { useRef, useEffect } from "react";
const DynamicComponent = React.forwardRef(({ name }, ref) => {
// Some component logic here...
return <div ref={ref}>{name}</div>;
});
const HOC = ({ dynamicComponentProps }) => {
const dynamicRefs = useRef(
dynamicComponentProps.map(() => ({ current: null }))
);
useEffect(() => {
dynamicRefs.current.forEach((ref) => {
console.log(ref.current); // This should log the div element
// Some other logic with ref.current...
});
}, [dynamicRefs.current]);
const renderDynamicComponent = () => {
return dynamicComponentProps.map((props, index) => (
<DynamicComponent
ref={dynamicRefs.current[index]}
key={index}
{...props}
/>
));
};
return <div>{renderDynamicComponent()}</div>;
};
const App = () => {
const dynamicComponentProps = [
{ name: "Component A" },
{ name: "Component B" },
{ name: "Component C" },
];
return <HOC dynamicComponentProps={dynamicComponentProps} />;
};
export default App;