Search code examples
reactjsreact-forwardref

How to pass ref as a prop in object of component?


I am accessing availableWidgets as a widget?.component in GridItem to iterate component but now I want to pass GridItem Ref to widget components. Give me Suggestion you any.

const availableWidgets = [
  {},
  {
    component: <DashboardsGuide />,
    id: "1"
  },

  {
    component: <DashboardsWeather />,
    id: "2"
  },
  {
    component: <DashboardsTraining />,
    id: "3"
  },
];

Here, the available widgets content the component and I want to pass parent ref.current into this widget but I am not getting idea how to pass ref to its child component while iterating it.

If it is possible then give fruitful suggestion

I had tried forwardRef to make it possible but not get succeed.

const GridItem = React.forwardRef(function GridItem(
  { className, style, children, item, widget, id, ...rest },
  ref
) {
  return (
    <div
      id={`width-height-${id}`}
      className={`grid-item ${className}`}
      style={style}
      ref={ref}
      {...rest}
    >
      {console.log(ref?.current?.offsetHeight, ref?.current?.offsetWidth)}
      {widget?.component}
      {children}
    </div>
  );
});

I am using React Grid Layout library.

function GridLayout({ layouts, toggle }) {
  return (
    <ResponsiveGridLayout
      // className="layout"
      layouts={layouts}
      rowHeight={20}
      isDraggable={toggle ? true : false}
      isResizable={toggle ? true : false}
      margin={[20, 20]}
      onLayoutChange={(...e) => {
        localStorage.setItem("layouts", JSON.stringify(e[1]));
      }}
      breakpoints={{ lg: 1280, md: 992, sm: 767, xs: 480, xxs: 0 }}
      cols={{ lg: 24, md: 20, sm: 12, xs: 8, xxs: 4 }}
    >
      {layouts.lg.map((item) => (
        <GridItem id={item.i} key={item.i} widget={availableWidgets[item.i]} />
      ))}
    </ResponsiveGridLayout>
  );
}

By using

{layouts.lg.map((item) => (
    <GridItem id={item.i} key={item.i} widget={availableWidgets[item.i]} />
  ))}

I am iterating widgets


Solution

  • If I'm understanding correctly, GridItem needs to create a React ref and attach it to the div it's rendering, and pass the ref along to the child widget component to access the current value of (i.e. the offset).

    For this I suggest a small change to the availableWidgets array so as to allow you to instantiate the components as JSX when rendering and inject any props then.

    const availableWidgets = [
      {},
      {
        component: DashboardsGuide,
        id: "1"
      },
    
      {
        component: DashboardsWeather,
        id: "2"
      },
      {
        component: DashboardsTraining,
        id: "3"
      },
    ];
    

    Next have GridItem create the React ref, attach it to the div and instantiate the widget component to pass the ref in a prop.

    const GridItem = ({
      className,
      style,
      children,
      item,
      widget = {},
      id,
      ...rest
    }) => {
      // (1) Create a React ref
      const gridItemRef = React.useRef();
    
      // (3) get widget component and rename to valid PascalCase name
      const { component: Widget } = widget;
    
      // Log the ref values in an intentional side-effect
      React.useEffect(() => {
        const { offsetHeight, offsetWidth } = gridItemRef.current;
        console.log(offsetHeight, offsetWidth);
      });
    
      return (
        <div
          id={`width-height-${id}`}
          className={`grid-item ${className}`}
          style={style}
          ref={gridItemRef} // (2) attach ref
          {...rest}
        >
          {Widget && <Widget gridItemRef={gridItemRef} />} // (4) render Widget and pass ref prop
          {children}
        </div>
      );
    };
    

    Inside each widget component (DashboardsGuide, DashboardsWeather, and DashboardsTraining) access the passed GridItem's gridItemRef via props.