'How can I use the same setState for two different tasks?
EDIT*** THE CODE IN THE SNIPPET IS NOT MEANT TO BE RUN BUT ONLY DISPLAY MY CODE, SORRY FOR THE CONFUSION
I am working a slider for an e-commerce app. Making each slider map over my useState array to create the different cards and the useState is receiving data from my fetched JSON server.
Now I am stuck at a brick wall in figuring how to use the same state to handle rendering the image onto my slider & click on the arrow to show the new slide.
I realize that I should update the state but I feel that I am over (or under) thinking things on how to do so.
My code snippets will show one, my code & two my JSON server data.
I receive an "uncaught error: too many re-renders." React limits number of renders to prevent an infinite loop
Can somebody please walk me through what I need to do here to complete this task? Thanks!
import {useState, useEffect} from 'react';
import { ArrowLeftOutlined, ArrowRightOutlined } from "@material-ui/icons";
import styled from "styled-components";
import { set } from 'react-hook-form';
const Container = styled.div`
width: 100%;
height: 95vh;
display: flex;
// background-color: #b3f0ff;
position: relative;
overflow: hidden;
`;
const Arrow = styled.div`
width: 50px;
height: 50px;
background-color: #e6ffff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
top: 0;
bottom: 0;
left: ${(props) => props.direction === "left" && "10px"};
right: ${(props) => props.direction === "right" && "10px"};
margin: auto;
cursor: pointer;
opacity: 0.5;
z-index: 2;
`;
const Wrapper = styled.div`
height: 100%;
display: flex;
transform: translateX(${props => props.slideIndx.data2 * -100}vw);
`
const Slide = styled.div`
width: 100vw;
height: 100vw;
display: flex;
align-items: center;
background-color: ${props => props.bg};
`
const ImgContainer = styled.div`
height: 100%;
flex:1;
`
const Image = styled.img`
padding-left: 30px;
align-items: left;
`
const InfoContainer = styled.div`
height: 80%;
flex:1;
padding: 30px;
`
const Title = styled.h1`
font-size: 50px
`
const Desc = styled.p`
margin: 50px 0px;
font-size: 20px;
font-weight: 500;
letter-spacing: 3px;
`
const Button = styled.button`
padding: 10px;
font-size: 20px;
background-color: transparent;
cursor: pointer;
`
const Slider = () => {
const [slideIndx, setSlideIndx] = useState({data1:[], data2: 0});
const handleClick = (direction) => {
if(direction === "left"){
setSlideIndx(slideIndx.data2 > 0 ? slideIndx.data2 - 1 : 2);
} else{
setArrowIndx(slideIndx.data2 < 2 ? slideIndx.data2 + 1 : 0);
}
};
const newArray = []
setSlideIndx((slideIndx) => ({
...slideIndx,
data2: [...newArray]
}));
const fetchSliderItems = (id) => {
fetch('http://localhost:3000/sliderItems')
.then(resp => resp.json())
.then(data => {
console.log(data)
setSlideIndx(data)
})
}
useEffect(() => {fetchSliderItems()}, [])
return (
<Container>
<Arrow direction="left" onClick={() => handleClick("left")}>
<ArrowLeftOutlined />
</Arrow>
<Wrapper slideIndx={slideIndx.data2}>
{slideIndx.data1.map((item) => (
<Slide bg={item.bg}>
<ImgContainer>
<Image src={item.img}/>
</ImgContainer>
<InfoContainer>
<Title>{item.title}</Title>
<Desc>{item.desc}</Desc>
<Button>SHOP NOW</Button>
</InfoContainer>
</Slide>
))}
</Wrapper>
<Arrow direction="right" onClick={() => handleClick("right")}>
<ArrowRightOutlined />
</Arrow>
</Container>
)
}
export default Slider
{
"sliderItems": [
{
"id": 1,
"img": "../images/model1.png",
"title": "SPRING CLEANING",
"desc": "DONT MISS OUR BEST COLLECTION YET! USE #FLATIRON10 TO RECEIVE 10% OFF YOUR FIRST ORDER",
"bg": "#b3ecff"
},
{
"id": 2,
"img": "../images/model2.png",
"title": "SHOW OFF HOW YOU DRESS",
"desc": "WITH OUR HUGE SELECTION OF CLOTHES WE FIT ALL YOUR STYLING NEEDS",
"bg": "#ccf2ff"
},
{
"id": 3,
"img": "../images/model3.png",
"title": "POPULAR DEALS",
"desc": "RECEIVE FREE SHIPPING ON ALL ORDERS OVER $50!",
"bg": "#fe6f9ff"
}
]
}
Solution 1:[1]
It is totally possible to do this with one useState, two useStates and my personal preference, useReducer.
I'll just give an example. I haven't tried running this code, as I don't have the components. So treat it like pseudo code:
const initialState = {
selectedIndex: 0,
"sliderItems": [
{
"id": 1,
"img": "../images/model1.png",
"title": "SPRING CLEANING",
"desc": "DONT MISS OUR BEST COLLECTION YET! USE #FLATIRON10 TO RECEIVE 10% OFF YOUR FIRST ORDER",
"bg": "#b3ecff"
},
{
"id": 2,
"img": "../images/model2.png",
"title": "SHOW OFF HOW YOU DRESS",
"desc": "WITH OUR HUGE SELECTION OF CLOTHES WE FIT ALL YOUR STYLING NEEDS",
"bg": "#ccf2ff"
},
{
"id": 3,
"img": "../images/model3.png",
"title": "POPULAR DEALS",
"desc": "RECEIVE FREE SHIPPING ON ALL ORDERS OVER $50!",
"bg": "#fe6f9ff"
},
],
}
function reducer(state, action) {
switch (action.type) {
case 'setData':
return {
selectedIndex: 0,
sliderItems: action.sliderItems,
};
case 'slideRight':
return {
...state,
// this assumes you want to go back to index 0 if you slide right on last item
selectedIndex: state.sliderItems.length - state.selectedIndex > 1 ? state.selectedIndex + 1 : 0,
};
case 'slideLeft':
return {
...state,
// this assumes you want to go to last item if sliding left on first item
selectedIndex: state.selectedIndex === 0 ? state.sliderItems.length - 1 : state.selectedIndex - 1,
};
default:
return state;
}
}
const Example = () => {
const [{ sliderItems, selectedIndex }, dispatch] = useReducer(initialState, reducer);
return (
<Container>
<Arrow direction="left" onClick={() => dispatch({ type: 'slideLeft' })}>
<ArrowLeftOutlined />
</Arrow>
<Wrapper slideIndx={selectedIndex}>
{sliderItems.map((item) => (
<Slide bg={item.bg}>
<ImgContainer>
<Image src={item.img}/>
</ImgContainer>
<InfoContainer>
<Title>{item.title}</Title>
<Desc>{item.desc}</Desc>
<Button>SHOP NOW</Button>
</InfoContainer>
</Slide>
))}
</Wrapper>
<Arrow direction="right" onClick={() => dispatch({ type: 'slideRight' })}>
<ArrowRightOutlined />
</Arrow>
</Container>
)
}
If you choose to try this approach, just dispatch a setData action in your fetch handler. That will reset the state with new items and start at index 0. The behavior can of course be modified to your liking.
dispatch({
type: 'setData',
sliderItems: fetchResult.sliderItems,
})
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |

