I am stuck on an issue with my SlideShow with Redux , which I am new with. I am trying to figure out the logic to connect my dropdown select and the next and previous buttons so that redux keeps track of my state. I want the user to be able to navigate through the slides, and also be able to select a slide from the dropdown. I cant figure out what I should do to connect it all but i think i'm close... :\
My Slides
const SlideData = [
{
title: "Introduction",
content: "This is some content",
},
{
title: "Slide 2",
content: "This is some content",
},
{
title: "Slide 3",
content: "This is some content",
},
{
title: "Slide 4",
content: "This is some content",
},
];
export default SlideData;
REDUX FILE
import { createSlice, configureStore } from "@reduxjs/toolkit";
import SlideData from "../SlideData";
//SET INITIAL STATE//
const initialSlideState = {
SlideData,
totalSlides: SlideData.length,
currentSlide: 0,
progress: 0,
};
//SET REDUCERS//
const slideSlice = createSlice({
name: "slide",
initialState: initialSlideState,
reducers: {
setCurrentSlide(state, action) {
state.currentSlide = SlideData[action.payload];
},
nextSlide(state) {
if (state.currentSlide < state.totalSlides) {
state.currentSlide++;
}
},
prevSlide(state) {
if (state.currentSlide > 0) {
state.currentSlide--;
}
},
setProgress(state, action) {
state.progress = action.payload;
},
},
});
//CREATE STORE//
const store = configureStore({
reducer: slideSlice.reducer,
});
export const slideActions = slideSlice.actions;
export default store;
SLIDESHOW FILE
//IMPORTS//
import React, { useRef } from "react";
import styles from "./Slideshow.module.css";
import Button from "./UI/Button";
import { Slide } from "react-slideshow-image";
import SlideData from "../SlideData";
import { useSelector, useDispatch } from "react-redux";
import { slideActions } from "../store/index";
import "react-slideshow-image/dist/styles.css";
export default function Slideshow() {
const dispatch = useDispatch();
const slideRef = useRef();
const currentSlide = useSelector((state) => state.currentSlide);
const totalSlides = useSelector((state) => state.totalSlides);
//SET CURRENT SLIDE
const setCurrentSlideHandler = (index) => {
dispatch(slideActions.setCurrentSlide(index));
};
//NEXT SLIDE
const nextSlideHandler = () => {
dispatch(slideActions.nextSlide());
dispatch(slideActions.setProgress((currentSlide / totalSlides) * 100));
slideRef.current.goNext();
};
//PREVIOUS SLIDE
const prevSlideHandler = () => {
dispatch(slideActions.prevSlide());
dispatch(slideActions.setProgress((currentSlide / totalSlides) * 100));
slideRef.current.goBack();
};
//GO TO SLIDE (DROPDOWN)<<--feel like i'm close here?
const goto = ({ target }) => {
console.log("Before: " + currentSlide);
let info = parseInt(target.value, 10);
console.log(info);
setCurrentSlideHandler(info);
slideRef.current.goTo(currentSlide);
console.log("after: " + currentSlide);
};
//SLIDESHOW PROPS//
const properties = {
transitionDuration: 200,
autoplay: false,
arrows: false,
};
//MAP DATA FOR DROPDOWN//
const options = SlideData.map((item, index) => (
<option key={index} value={index}>
{item.title}
</option>
));
return (
<div className={styles.container}>
<Slide ref={slideRef} {...properties}>
{SlideData.map((item, index) => (
<div key={index} className={styles.slide}>
<h2>{item.title}</h2>
<p>{item.content}</p>
</div>
))}
</Slide>
<div>
<Button type="button" onClick={prevSlideHandler}>
Back
</Button>
<Button type="button" onClick={nextSlideHandler}>
Next
</Button>
<select className={styles.select} onChange={goto}>
<option>--Select--</option>
{options}
</select>
</div>
</div>
);
}
I think the problem is that in your component you are BOTH using the methods provided by the library AND trying to do things based on redux state. If you are trying to set the slides using ONLY redux, then what you want is, upon hitting the arrow to go forward or back, to dispatch the (new) "currentSlide" (index) to redux, and then use useEffect(())
to listen for changes to the currentSlide
selector. When the value changes, you'll use the updated value to call the libraries method goTo(index)
with that new index.
That would look like: (disclaimer: I'm removing things that don't matter to the points I'm making)
import { createSlice, configureStore } from "@reduxjs/toolkit";
import SlideData from "../SlideData";
//SET INITIAL STATE//
const initialSlideState = {
SlideData,
totalSlides: SlideData.length,
currentSlide: 0,
};
//SET REDUCERS//
const slideSlice = createSlice({
name: "slide",
initialState: initialSlideState,
reducers: {
setCurrentSlide(state, action) {
state.currentSlide = action.payload;
},
nextSlide(state) {
if (state.currentSlide < state.totalSlides) {
state.currentSlide++;
}
},
prevSlide(state) {
if (state.currentSlide > 0) {
state.currentSlide--;
}
},
},
});
//IMPORTS//
import React, { useRef } from "react";
import styles from "./Slideshow.module.css";
import Button from "./UI/Button";
import { Slide } from "react-slideshow-image";
import SlideData from "../SlideData";
import { useSelector, useDispatch } from "react-redux";
import { slideActions } from "../store/index";
import "react-slideshow-image/dist/styles.css";
export default function Slideshow() {
const dispatch = useDispatch();
const slideRef = useRef();
const currentSlide = useSelector((state) => state.currentSlide);
const totalSlides = useSelector((state) => state.totalSlides);
useEffect(() => {
slideRef.current.goTo(currentSlide)
}, currentSlide)
//SET CURRENT SLIDE
const setCurrentSlideHandler = (index) => {
dispatch(slideActions.setCurrentSlide(index));
};
//NEXT SLIDE
const nextSlideHandler = () => {
dispatch(slideActions.nextSlide());
};
//PREVIOUS SLIDE
const prevSlideHandler = () => {
dispatch(slideActions.prevSlide());
};
//GO TO SLIDE (DROPDOWN)<<--feel like i'm close here?
const goto = ({ target }) => {
let info = parseInt(target.value, 10);
setCurrentSlideHandler(info);
};
//SLIDESHOW PROPS//
const properties = {
transitionDuration: 200,
autoplay: false,
arrows: false,
};
//MAP DATA FOR DROPDOWN//
const options = SlideData.map((item, index) => (
<option key={index} value={index}>
{item.title}
</option>
));
return (
<div className={styles.container}>
<Slide ref={slideRef} {...properties}>
{SlideData.map((item, index) => (
<div key={index} className={styles.slide}>
<h2>{item.title}</h2>
<p>{item.content}</p>
</div>
))}
</Slide>
<div>
<Button type="button" onClick={prevSlideHandler}>
Back
</Button>
<Button type="button" onClick={nextSlideHandler}>
Next
</Button>
<select className={styles.select} onChange={goto}>
<option>--Select--</option>
{options}
</select>
</div>
</div>
);
}
With the above, no matter which way you go (forward, backward, or selecting explicitly), the appropriate action is dispatched and the currentSlide
(INDEX) is updated in your state, from which you are getting your selector value. When the component sees that the selector value has changed, it will call the library's goTo(index)
method with that value.