ReactJS problem with changing direction of translateX() transformation

Solution for ReactJS problem with changing direction of translateX() transformation
is Given Below:

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>

Answer was found.

Setting a state is not synchronous and there is no reason to store style in an additional state hook since it’s just one parameter and can be passed in the render function imemdiately.

    const [xPos, setXPos ] = React.useState(0);
  
    const onClick =(direction) => {
       (direction === "left") ? setXPos(x => x-100) : setXPos(x => x +100);
    }

    return (
     <div className="main_container">
     <button className="left_button" onClick={() => onClick("left")}>slide left</button>
     <div className="forecast_slider" >
       <div className="forecast_container" style={{transform : `translateX(${xPos}px)`}} > 
        {forecastBuilder()}
       </div>
     </div>
      <button className="right_button" onClick={() => onClick("right")}>slide right</button>
     </div>
  )

fixes the problem.