1

The buttons i create using below seems to lag in the selectedButtonIdx value.

Is the toggleSelected not complete by the time getClass is called ?

function ButtonGroup(props) {
    const [selectedButtonIdx,setIdx]=useState(props.loadCurrentAsIndex);

    const toggleSelected = (e) => {
        setIdx(parseInt(e.target.dataset.index));
        props.onclick(e);
    };

    const getClass = (index) => {
        return (selectedButtonIdx === index) ? classnames('current', props.btnClass)
            : classnames(props.btnClass)
    };

    let buttons = props.buttons.map((b, idx) => <Button key={idx} value={b.value} index={idx} text={b.text}
                                                        onclick={e => toggleSelected(e)}
                                                        btnClass={getClass(idx)}/>);

    return (
        <div>
            {buttons}
        </div>
    );
}

Every onclick is expected to show the user which button in the group was clicked by changing its class.

ravibagul91
  • 20,072
  • 5
  • 36
  • 59
bonney
  • 537
  • 4
  • 15
  • 1
    This is most likely because `useState` is asynchronous [as answered here](https://stackoverflow.com/a/54069332/11929924). – deafening_roar Aug 15 '19 at 05:35

3 Answers3

1

By looking at this,

<Button 
  key={idx} 
  value={b.value} 
  index={idx} 
  text={b.text}
  onclick={e => toggleSelected(e)}
  btnClass={getClass(idx)}
/>

Button is your custom component,

Two things to notice here,

  1. You have provided onclick (c is small) props, in you actual component it should be onClick={props.onclick}
  2. You have used e.target.dataset.index, to work with dataset we should have attribute with data- prefix. So your index should be data-index in your actual component.

So finally your Button component should be,

const Button = (props) => {
  return <button text={props.text} data-index={props.index} onClick={props.onclick} className={props.btnClass}>{props.value}</button>
}

Demo

ravibagul91
  • 20,072
  • 5
  • 36
  • 59
0

The function setIdx, returned from useState is asynchronous, this means that it may be not be finished by the time you run your next function (as you guessed).

Take a look at useEffect it allows you to specify a function to run once an item in your state changes, this method will ensure your functions are called in the right order.

MarcusOuelletus
  • 150
  • 2
  • 8
0

By now I don't see anything wrong here.

How it works:

  1. initial render happens, onClick event listener is bound
  2. user clicks a button, event handler calls setIdx triggering new render
  3. new render is initiated, brand new selectedButtonIdx is used for rendering(and for getClass call as well)

See, there is no reason to worry about if setIdx is sync function or async.

skyboyer
  • 22,209
  • 7
  • 57
  • 64