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 to4
)startAt
is a state-variable (initially set to 0)data
is rendered onreturn
only when it is anArray
switchImages
is a method invoked on bothleft-arrow
andright-arrow
click-events (changing the parameterdirection
toreverse
onleft-arrow
)startAt
is updated based on thedirection
, theprev
value, thetotalImages
andimageLength
.
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:
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 }) => {
...
}