Given :
function App() {
const [xPos, setXPos ] = React.useState(0);
const [style, setStyle] = React.useState({transform: `translateX(${xPos}px)`});
const onClick =(direction) => {
(direction === "left") ? setXPos(x => x-100) : setXPos(x => x +100);
setStyle({transform: `translateX(${xPos}px)`});
console.log(xPos)
}
return (
<div className="main_container">
<button className="left_button" onClick={() => onClick("left")}>slide left</button>
<div className="forecast_slider" >
<div className="forecast_container" style={style} >
{forecastBuilder()}
</div>
</div>
<button className="right_button" onClick={() => onClick("right")}>slide right</button>
</div>
)
}
const forecastBuilder = () => {
const cell = [];
for(var i = 1 ; i < 8 ; i++){
cell.push(
<div className={i}>
{i}
<img src="https://imgs.michaels.com/MAM/assets/1/5E3C12034D34434F8A9BAAFDDF0F8E1B/img/0E9397ED92304202B4A25D7387A74515/M10118706_2.jpg" width="100" height="80" border="1px solid black" />
<br></br>
<span>day {i}</span>
</div>
)
}
return cell;
}
ReactDOM.render(<App />, document.querySelector("#app"));
.main_container {
display:flex;
}
.forecast_container {
display: flex;
width: 510px;
height: 130px;
margin-left: auto;
margin-right: auto;
align-items: center;
text-align: center;
transition: transform 250ms;
}
.forecast_slider {
background-color: black;
color: white;
overflow:hidden;
float:right;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>
with JSFiddle link here ,
I want to make the translateX()
animation increment and decrement normally. Currently, I have to click twice a button to change the direction. I have no clue why this is happening. I tried setting the initial style
's transform parameter to 0px
. I haven't tried other things since honestly, I am short of ideas, this bug is beyond my understanding of React.
Does anyone have any idea how I could solve this?
The problem is you try to use the updated xPos
state in your onClick
handler right after you "updated" it: setStyle({transform: `translateX(${xPos}px)`})
Don't forget that useState
is asynchronous just like setState
in class components. You can't update the state on one line and assume it's already changed on the next one. You'll likely use the unchanged state.
Create a new variable and update both states using that one:
function App() {
const [xPos, setXPos] = React.useState(0);
const [style, setStyle] = React.useState({
transform: `translateX(${xPos}px)`,
});
const onClick = (direction) => {
let x = direction === 'left' ? xPos - 100 : xPos + 100
setXPos(x)
setStyle({ transform: `translateX(${x}px)` });
console.log(xPos);
};
return (
<div className="main_container">
<button className="left_button" onClick={() => onClick('left')}>
slide left
</button>
<div className="forecast_slider">
<div className="forecast_container" style={style}>
{forecastBuilder()}
</div>
</div>
<button className="right_button" onClick={() => onClick('right')}>
slide right
</button>
</div>
);
}
const forecastBuilder = () => {
const cell = [];
for (var i = 1; i < 8; i++) {
cell.push(
<div className={i}>
{i}
<img
src="https://imgs.michaels.com/MAM/assets/1/5E3C12034D34434F8A9BAAFDDF0F8E1B/img/0E9397ED92304202B4A25D7387A74515/M10118706_2.jpg"
width="100"
height="80"
border="1px solid black"
/>
<br></br>
<span>day {i}</span>
</div>
);
}
return cell;
};
ReactDOM.render(<App />, document.querySelector('#app'));
.main_container {
display: flex;
}
.forecast_container {
display: flex;
width: 510px;
height: 130px;
margin-left: auto;
margin-right: auto;
align-items: center;
text-align: center;
transition: transform 250ms;
}
.forecast_slider {
background-color: black;
color: white;
overflow: hidden;
float: right;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Also, you could simply get rid of the useStyles
hook as it's just some extra fluff around xPos
:
function App() {
const [xPos, setXPos] = React.useState(0);
return (
<div className="main_container">
<button className="left_button" onClick={() => setXPos(xPos - 100)}>
slide left
</button>
<div className="forecast_slider">
<div className="forecast_container" style={{ transform: `translateX(${xPos}px)` }}>
{forecastBuilder()}
</div>
</div>
<button className="right_button" onClick={() => setXPos(xPos + 100)}>
slide right
</button>
</div>
);
}
const forecastBuilder = () => {
const cell = [];
for (var i = 1; i < 8; i++) {
cell.push(
<div className={i}>
{i}
<img
src="https://imgs.michaels.com/MAM/assets/1/5E3C12034D34434F8A9BAAFDDF0F8E1B/img/0E9397ED92304202B4A25D7387A74515/M10118706_2.jpg"
width="100"
height="80"
border="1px solid black"
/>
<br></br>
<span>day {i}</span>
</div>
);
}
return cell;
};
ReactDOM.render(<App />, document.querySelector('#app'));
.main_container {
display: flex;
}
.forecast_container {
display: flex;
width: 510px;
height: 130px;
margin-left: auto;
margin-right: auto;
align-items: center;
text-align: center;
transition: transform 250ms;
}
.forecast_slider {
background-color: black;
color: white;
overflow: hidden;
float: right;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>