Masonic React.js: width scrolling issue with Left Nav

Viewed 524

enter image description here

import * as React from 'react';
    import {
      Card,
      CardBody,
      CardLink,
      CardImg,
      CardTitle,
      CardText,
    } from 'reactstrap';
    import {useWindowSize} from '@react-hook/window-size';
    import {
      useMasonry,
      usePositioner,
      useContainerPosition,
      useScroller,
    } from 'masonic';
    
    const Gallery = ({items}) => {
      const containerRef = React.useRef(null);
      let [windowWidth, height] = useWindowSize();
      windowWidth = windowWidth - (0.3*windowWidth);
      const {offset, width} = useContainerPosition(containerRef, [
        windowWidth,
        height,
      ]);
      const positioner = usePositioner({width, columnGutter: 8});
      const {scrollTop, isScrolling} = useScroller(offset);
    
      const NatureCard = ({
        index,
        data: {image = '', displayName, description = '', link = ''},
        width,
      }) => (
        <div>
          <Card>
            <CardImg src={image} />
            <CardTitle tag="h5">{displayName}</CardTitle>
            <CardText>{description}</CardText>
            {link && <CardLink href={link}>View</CardLink>}
          </Card>
        </div>
      );
    
      return (
        <div>
          {useMasonry({
            positioner,
            scrollTop,
            isScrolling,
            height,
            containerRef,
            items: items,
            overscanBy: 3,
            rowGutter: 10,
            render: NatureCard,
          })}
        </div>
      );
    };
    
    export default Gallery;

I am using masonic and above code however my data is getting rearranged very weird across the width rather than the height

The library being used: https://codesandbox.io/s/masonryscroller-example-hb3qj

EDIT: I think the issue is with how I retrieve my items

CodeSandbox here: https://codesandbox.io/s/masonryscroller-example-hb3qj?file=/src/index.js

2 Answers

The problem is that when the images are fetched by the browser the height of the cards change so their position should be recalculated. This is done using the useResizeObserver which creates a resize observer that forces updates to the grid cell positions when mutations are made to cells affecting their height. Now whenever you resize the component it will render correctly. There is another issue in initial render width is 0 so the items aren't positioned correctly. I used windowWidth as a placeholder until width is set.

You can check it here in the docs: https://github.com/jaredLunde/masonic#useresizeobserverpositioner

The asnwer CodeSandbox

The answer:

import React from "react";
import ReactDOM from "react-dom";
import { useWindowSize } from "@react-hook/window-size";
import { Card, CardImg, CardText, CardTitle } from "reactstrap";
import { styles } from "./theme";
import {
  useMasonry,
  usePositioner,
  useContainerPosition,
  useScroller,
  // import this
  useResizeObserver
} from "masonic";

const App = () => {
  const containerRef = React.useRef(null);
  let [windowWidth, windowHeight] = useWindowSize();
  const { offset, width } = useContainerPosition(containerRef, [
    windowWidth,
    windowHeight
  ]);
  // the width is equal to windowWidth - scroll bar width (17px in chrome)
  console.log(width, windowWidth);
  const positioner = usePositioner({
    // Use windowWidth as a placeholder if container width is 0 which causes positioning problems
    // This happens in initial render then width will be used
    width: width || windowWidth,
    columnGutter: 10
  });
  // This hook creates a resize observer that forces updates to the grid
  // cell positions when mutations are made to cells affecting their height.
  const resizeObserver = useResizeObserver(positioner);
  const { scrollTop, isScrolling } = useScroller(offset);

  return (
    <main>
      <div>
        {useMasonry({
          positioner,
          scrollTop,
          isScrolling,
          height: windowHeight,
          containerRef,
          items: items,
          render: NatureCard,
          // use it here
          resizeObserver
        })}
      </div>
      <Header />
    </main>
  );
};

const NatureCard = ({
  index,
  data: { image = "", displayName, description = "Hello", link = "" },
  width,
  height
}) => (
  <Card>
    <CardImg src={image} />
    <CardTitle tag="h5">{displayName}</CardTitle>
    <CardText>{description}</CardText>
  </Card>
);

const Header = () => {
  // const scrollY = useWindowScroll(5);
  return (
    <h1>
      <span role="img" aria-label="bricks">
        
      </span>{" "}
      MASONIC
    </h1>
  );
};

const items = [
  {
    image:
      "https://hips.hearstapps.com/hmg-prod.s3.amazonaws.com/images/gettyimages-647727888-master-1506626232.jpg",
    displayName: "QUIZ: Has Reese Witherspoon Smooched These Guys On-Screen?"
  },
  {
    image:
      "https://ichef-1.bbci.co.uk/news/320/cpsprodpb/97BE/production/_98064883_ikea.jpg",
    displayName: "Ikea buys odd jobs firm TaskRabbit"
  },
  {
    image:
      "https://cdn.theatlantic.com/assets/media/img/mt/2017/09/ra_3000x3000-4/facebook.jpg",
    displayName:
      "Radio Atlantic: Russia! Live with Julia Ioffe and Eliot A. Cohen"
  },
  {
    image: "https://i.redd.it/th5enkjgumoz.jpg",
    displayName: "Bar"
  },
  {
    image: "https://imgur.com/lvK8Faf.jpg",
    displayName:
      "'Cancer patient' finds lump was toy traffic cone inhaled in 1977 | UK news"
  },
  {
    image: "https://i.imgur.com/e89Pb8j.gif",
    displayName: "It's cool bro, I got this"
  },
  {
    image: "https://i.redd.it/336erdiy5moz.jpg",
    displayName: "Reddit"
  },
  {
    image:
      "https://hips.hearstapps.com/hmg-prod.s3.amazonaws.com/images/img-5675-jpg-1506621753.jpg",
    displayName:
      "Prepare to Geek Out Over This Adorable Game of Thrones Wedding Shower"
  },
  {
    image:
      "https://venturebeat.com/wp-content/uploads/2017/03/ShopChat-e1506572472915.jpg",
    displayName: "Effective chatbots master conversational size and fit"
  },
  {
    image:
      "https://hips.hearstapps.com/hmg-prod.s3.amazonaws.com/images/gettyimages-647727888-master-1506626232.jpg",
    displayName: "QUIZ: Has Reese Witherspoon Smooched These Guys On-Screen?"
  },
  {
    image:
      "https://ichef-1.bbci.co.uk/news/320/cpsprodpb/97BE/production/_98064883_ikea.jpg",
    displayName: "Ikea buys odd jobs firm TaskRabbit"
  },
  {
    image:
      "https://cdn.theatlantic.com/assets/media/img/mt/2017/09/ra_3000x3000-4/facebook.jpg",
    displayName:
      "Radio Atlantic: Russia! Live with Julia Ioffe and Eliot A. Cohen"
  },
  {
    image: "https://i.redd.it/th5enkjgumoz.jpg",
    displayName: "Bar"
  },
  {
    image: "https://i.imgur.com/HjgUCHF.png",
    displayName:
      "'Cancer patient' finds lump was toy traffic cone inhaled in 1977 | UK news"
  },
  {
    image: "https://i.imgur.com/e89Pb8j.gif",
    displayName: "It's cool bro, I got this"
  },
  {
    image: "https://i.redd.it/336erdiy5moz.jpg?4",
    displayName: "Reddit"
  },
  {
    image:
      "https://hips.hearstapps.com/hmg-prod.s3.amazonaws.com/images/img-5675-jpg-1506621753.jpg",
    displayName:
      "Prepare to Geek Out Over This Adorable Game of Thrones Wedding Shower"
  },
  {
    image:
      "https://venturebeat.com/wp-content/uploads/2017/03/ShopChat-e1506572472915.jpg",
    displayName: "Effective chatbots master conversational size and fit"
  },
  {
    image: "https://i.imgur.com/e89Pb8j.gif",
    displayName: "It's cool bro, I got this"
  },
  {
    image: "https://i.redd.it/336erdiy5moz.jpg?4",
    displayName: "Reddit"
  },
  {
    image:
      "https://hips.hearstapps.com/hmg-prod.s3.amazonaws.com/images/img-5675-jpg-1506621753.jpg",
    displayName:
      "Prepare to Geek Out Over This Adorable Game of Thrones Wedding Shower"
  }
];

ReactDOM.render(<App />, document.getElementById("root"));

I have take a look at your code sanbox and the one here, too.

Your problem is:

  • If you use useMasonry, you MUST specify the height property for each item. You can see that in above example code sanbox, it generates random height for demo and use it to apply style for grid item via style={{ height: props.data.height }}.
  • You you use Masonry component instead, you MUST specify the columnWidth property. In other words, this approach is just a kind of above one, just different from specifying width and height.

My working code sandbox here.

You can try to play around with two approach in my code sandbox to understand more. By the way, the @-ui/react library is DEPRECATED also.

I suggest another library you can investigate on is bricks.js: http://callmecavs.com/bricks.js/

Related