Home > Back-end >  Create a function that returns the next/prev 4 results in an array of objects
Create a function that returns the next/prev 4 results in an array of objects

Time:03-03

I'm trying to create a function that renders the next & prev 4 results in an array of objects onClick. Currently I am only returning the first 4 items and their images and adjusting the values in the return statement onClick and am not happy with this solution. Any pointers?

const ImageSlider = ({ loadData, data, setData }) => {
  const [current, setCurrent] = useState(0);
  const length = data.length;
  const [test, setTest] = useState(0);
  const [another, setAnother] = useState(4);

  useEffect(() => {
    loadData().then((data) => setData(data));
  }, []);

  const getNextSlide = () => {
    setCurrent(current === length - 1 ? 0 : current   1);
    if (current / 3 === 1) {
      setTest(test   4);
      setAnother(another   4);
    }
  };

  const getPrevSlide = () => {
    setCurrent(current === 0 ? length - 1 : current - 1);
    // setTest(test - 1);
    // setAnother(another - 1);
  };

  console.log(current);

  if (!Array.isArray(data) || length <= 0) {
    return null;
    // Need that error message here
  }

  return (
    <div className="slider">
      <FaArrowCircleLeft className="slider-left-arrow" onClick={getPrevSlide} />
      <FaArrowCircleRight
        className="slider-right-arrow"
        onClick={getNextSlide}
      />
      {data.slice(test, another).map((program, index) => {
        return (
          <div
            className={
              index === current
                ? "slider-active-program"
                : "slider-inactive-program"
            }
            key={index}
          >
            <img
              src={program.image}
              alt="program"
              className="slider-program-image"
            />
          </div>
        );
      })}
    </div>
  );
};

export default ImageSlider;

CodePudding user response:

This may be one possible approach to achieve the desired objective:

const ImageSlider = ({ loadData, data, setData }) => {
  const imageLength = 4;    // number of images to be shown per-page
  const totalImages = data.length;
  const [startAt, setStartAt] = useSate(0);  // start rendering from image-0
  useEffect(() => {
    loadData().then((data) => setData(data));
  }, []);
  const switchImages = (direction = 'forward') => (
    setStartAt(prev => {
      if (direction === 'forward') {
        // if there are more images, reposition 'startAt' to next set
        if (prev   imageLength < totalImages) return prev   imageLength;
        return 0; // reset back to image-0
      } else {
        // if there is previous set, reposition to it's start
        if (prev - imageLength >= 0) return prev - imageLength;
        // otherwise, reposition to the start of the last-set
        return (Math.floor((totalImages - 1) / imageLength) * imageLength)
      };
    })
  );

  return (
    <div className="slider">
      <FaArrowCircleLeft
        className="slider-left-arrow"
        onClick={() => switchImages('reverse')}
      />
      <FaArrowCircleRight
        className="slider-right-arrow"
        onClick={() => switchImages()}
      />
      {Array.isArray(data) && data.filter((el, idx) => idx >= startAt && idx < (startAt   imageLength)).map((program, index) => {
        return (
          <div
            className={
              index === current
                ? "slider-active-program"
                : "slider-inactive-program"
            }
            key={index}
          >
            <img
              src={program.image}
              alt="program"
              className="slider-program-image"
            />
          </div>
        );
      })}
    </div>
  );
};

export default ImageSlider;

Explanation

  • imageLength is fixed (set to 4)
  • startAt is a state-variable (initially set to 0)
  • data is rendered on return only when it is an Array
  • switchImages is a method invoked on both left-arrow and right-arrow click-events (changing the parameter direction to reverse on left-arrow)
  • startAt is updated based on the direction, the prev value, the totalImages and imageLength.

Suggestion

If one needs to render a message to the user that the data array is either not-yet-loaded or it is empty, it may be acccomplished by changing the {Array.isArray(data) && .... into a conditional such as {Array.isArray(data) ? ....<existing> : <some message here>.

Warning

The above code is only an approach. It is not tested & may contain syntax errors or other issues. Please use comments to share feedback and this answer may be updated accordingly.

CodePudding user response:

I don't think you have to change much about your approach to get what you're looking for here.

To render the images to the left and right of your current selection and allow looping from the front to the back (as it looks like you want to do), we can paste a copy of the array to the front and back and select the middle slice. It's a little bit like this:

example image of tri duplicated array and taking a slice in the middle

The implementation would just require you to replace the current .map(...) method in your return statement with this one:

[...data, ...data, ...data].slice(
  length   current - peekLeft,
  length   current   peekRight   1
).map((program, index) => {
  ...
}

And then you can also remove the test and another state objects as well since they aren't necessary. You just need to add the peekLeft and peekRight or replace them inline inside the .map(...) statement. The first option may look something like this:

const peekLeft = 4;
const peekRight = 4;

const ImageSlider = ({ loadData, data, setData }) => {
  ...
}
  • Related