skip to Main Content

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.

2

Answers


  1. The issue in the provided React code is that the useRef is being assigned to each dynamically generated component in the loop, but useRef should be associated with a single element. To address this, you can modify the code to use an array of refs, with each ref corresponding to a dynamically generated component. Here’s the modified code:

    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([]);
    
      useEffect(() => {
        // Access refs in the array
        dynamicRefs.current.forEach((ref, index) => {
          console.log(ref.current); // Refs are now correctly set
          // Some other logic with ref.current...
        });
      }, [dynamicRefs.current]);
    
      const renderDynamicComponent = () => {
        return dynamicComponentProps.map((props, index) => {
          dynamicRefs.current[index] = dynamicRefs.current[index] || React.createRef();
          return <DynamicComponent ref={dynamicRefs.current[index]} key={props.name} {...props} />;
        });
      };
    
      return <div>{renderDynamicComponent()}</div>;
    };
    
    const App = () => {
      const dynamicComponentProps = [
        { name: 'Component A' },
        { name: 'Component B' },
        { name: 'Component C' },
      ];
    
      return <HOC dynamicComponentProps={dynamicComponentProps} />;
    };
    

    In this solution:

    • We use React.forwardRef to forward the ref from DynamicComponent to the underlying div.
    • Instead of a single ref (dynamicRef), we use an array of refs (dynamicRefs.current) to store each dynamically generated component’s ref.
    • We initialize each ref in the array only if it doesn’t exist yet.
    • The useEffect block correctly logs the refs, and you can perform operations on each component using the corresponding ref.

    This modification ensures that each dynamically generated component has its own ref, resolving the issue of dynamicRef.current always being null.

    Login or Signup to reply.
  2. 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

    1. changed DynamicComponent to forwardRef so that we can pass a ref
    2. in the useEffect we log the array of refs
    3. in renderDynamiccomponent we assign a different ref to each component

    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;
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search