2
\$\begingroup\$

I implemented this carousel component in react:

import React from "react";
import { AiOutlineArrowLeft, AiOutlineArrowRight } from "react-icons/ai";
import { useResizeDetector } from "react-resize-detector";

let arrowStyle = {
  background: "white",
  border: "1px solid lightgray",
  borderRadius: "20%",
  cursor: "pointer",
  display: "flex",
  justifyContent: "center",
  alignItems: "center",
  height: 30,
  width: 30,
  boxShadow: "rgba(0, 0, 0, 0.24) 0px 3px 8px",
};

export default function Carousel(props) {
  let [count, setCount] = React.useState(0);
  let innerContainer = React.useRef();
  
  // We use these coordinates to know if we should show left/right arrows or not
  let [relativeCords, setRelativeCords] = React.useState({});
  
  // We use this variable because during transition if user clicks multiple times the arrow buttons, coordinates are not computed correctly anymore
  let [transitionBlock, setTransitionBlock] = React.useState(false);

  let innerContainerRelativeCordsToParent = () => {
    let relativeLeft =
      innerContainer.current.getBoundingClientRect().left -
      ref.current.getBoundingClientRect().left;
    let relativeRight =
      ref.current.getBoundingClientRect().right -
      innerContainer.current.getBoundingClientRect().right;

    setRelativeCords({ relativeLeft, relativeRight });
  };

  const onResize = React.useCallback(() => {
    // When main container is resized we want to update relative coordinates
    innerContainerRelativeCordsToParent();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const { ref } = useResizeDetector({ onResize });

  React.useEffect(() => {
    innerContainerRelativeCordsToParent();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  React.useEffect(() => {
    // We need to run this also when transition ends to get fresh coordinates
    innerContainer.current.addEventListener("transitionend", () => {
      innerContainerRelativeCordsToParent();
      setTransitionBlock(false);
    });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return (
    <div className="carousel" style={{ padding: 50 }}>
      <div
        style={{
          paddingLeft: 50,
          position: "relative",
          paddingRight: 50,
        }}
      >
        <div
          ref={ref}
          style={{
            overflow: "hidden",
          }}
        >
          {/* If you have height issues with below div, see this: https://stackoverflow.com/questions/27536428/inline-block-element-height-issue */}
          <div
            ref={innerContainer}
            style={{
              display: "inline-block",
              whiteSpace: "nowrap",
              transition: "transform 0.4s linear",
              transform: `translateX(${-count * 100}px)`,
            }}
          >
            {props.items.map((x) => {
              return (
                <div
                  key={x.id}
                  style={{
                    padding: 5,
                    display: "inline-block",
                    width: 150,
                    height: 150,
                    marginLeft: 5,
                    marginRight: 5,
                    border: "1px solid lightgray",
                    borderRadius: 10,
                    overflow: "auto",
                    whiteSpace: "normal",
                  }}
                >
                  <h1>{x.title}</h1>
                  <p>{x.body}</p>
                </div>
              );
            })}
          </div>
        </div>
        <button
          style={{
            ...arrowStyle,
            position: "absolute",
            left: 0,
            top: "50%",
            transform: "translateY(-50%)",
          }}
          disabled={relativeCords.relativeLeft >= 0}
          onClick={() => {
            if (transitionBlock) return;
            setCount(count - 1);
            setTransitionBlock(true);
          }}
        >
          <AiOutlineArrowLeft />
        </button>
        <button
          style={{
            ...arrowStyle,
            position: "absolute",
            right: 0,
            top: "50%",
            transform: "translateY(-50%)",
          }}
          disabled={relativeCords.relativeRight >= 0}
          onClick={() => {
            if (transitionBlock) return;
            setCount(count + 1);
            setTransitionBlock(true);
          }}
        >
          <AiOutlineArrowRight />
        </button>
      </div>
    </div>
  );
}

Any feedback welcome. Here is demo: https://stackblitz.com/edit/react-vu1aqp?file=src%2Findex.js

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Some code pattern changes I guess?

I always try to keep using only variables declared with const when coding with this stack/architecture, since my goal is to keep data and state immutable.

I also changed the way you call React hooks like useState, it keeps the code cleaner and you lose the outdated class-y component inheritance pattern on modern js.

Take a look how everything still works and you prevent rewriting data/state later on

https://stackblitz.com/edit/react-elbtq9?file=src%2FApp.js

\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.