It seems that the reason might be Card
from antd
does not forward ref
, while ref
is needed for either react-intersection-observer
or react-grid-layout
to function.
While there is no official guide to keep these libraries work together, perhaps the <InView/>
component provided by react-intersection-observer
could be a possible approach, since it accepts forwardRef
, and can serve as a wrapper component for Card
, while it functions as the observer as well.
Potential drawbacks of this approach include <InView />
would be an extra wrapper for Card
, which might or might not be a problem depending on the use case.
Forked demo with the experimental approach: codesandbox
Card
with the implementation is separated as a custom component to keep the parent cleaner. The onChange
property on <InView />
can accept a callback to run what is needed when the component is in view.
A state isInView
is defined here just for testing, and I think a custom state would probably be a more suitable approach here than using the render props, which is limited inside <InView />
and also depends on ref
to function.
The props forwarded to the custom component are required by react-grid-layout
according to their document.
const MyCard = forwardRef(
(
{
style,
className,
onMouseDown,
onMouseUp,
onTouchEnd,
children,
...props
},
ref
) => {
// For testing only
const [isInView, setIsInView] = useState(false);
return (
<InView
threshold={0.3}
// For testing, accepts callback to run when in view
onChange={(inView, entry) => setIsInView(inView)}
ref={ref}
style={{ ...style }}
className={className}
onMouseDown={onMouseDown}
onMouseUp={onMouseUp}
onTouchEnd={onTouchEnd}
>
<Card
title="Small size card"
style={{
height: "100%",
border: isInView ? "5px solid hotpink" : "1px solid blue",
}}
>
<h2>{`30% inside viewport: ${isInView}.`}</h2>
<p>Card content</p>
</Card>
{children}
</InView>
);
}
);
Parent component now only include any logic to define layout
:
const App = () => {
const layout = [
{ i: "a", x: 0, y: 0, w: 8, h: 8 },
{ i: "b", x: 0, y: 5, w: 8, h: 8 },
{ i: "c", x: 0, y: 10, w: 8, h: 8 },
{ i: "d", x: 0, y: 10, w: 8, h: 8 },
{ i: "e", x: 0, y: 10, w: 8, h: 8 },
];
return (
<>
<GridLayout
className="layout"
layout={layout}
cols={12}
rowHeight={100}
width={800}
>
{/* Experimental approach */}
{layout.map((each) => (
<MyCard key={each.i || Math.random()} />
))}
</GridLayout>
</>
);
};
export default App;
On a side note, because <InView />
is already wired up with the required functionalities, the wrapped Card
from antd
could be replaced by a custom component with necessary styles, if preferred.