Inconsistent `setState` behavior between React and Preact (breaking react-select)
See original GitHub issueI’m using react-select with Preact (+ Preact-compat) and the arrow to open the drop down is not working. This is because of the following code in React Select:
handleMouseDownOnArrow (event) {
// if the event was triggered by a mousedown and not the primary
// button, or if the component is disabled, ignore it.
if (this.props.disabled || (event.type === 'mousedown' && event.button !== 0)) {
return;
}
// If the menu isn't open, let the event bubble to the main handleMouseDown
if (!this.state.isOpen) {
this.setState({
isOpen: true,
});
}
// prevent default event handlers
event.stopPropagation();
event.preventDefault();
// close the menu
if(this.state.isOpen){
this.closeMenu();
}
}
When trying to open the select and in React, the this.setState({ isOpen: true})
call will change the state async, whereas in Preact this happens immediately. This means that, in React, the expression in the line if(this.state.isOpen){
will be false
, but in Preact it’s true
. So state.isOpen
is flicked to true
and then straight back to false
before render()
is called. Meaning that in Preact this code has no effect.
It looks like this can be fixed with a simple if/else
(Unless there’s some deliberate reason those event.stop/prevent
calls are sandwiched in there). But I thought this was worth sharing since I can imagine react-select
is commonly used with Preact.
Is this subtle difference in behavior considered a Preact bug?
I guess it could also be fixed be proxying setState calls via setTimeout
in Preact-compat. Happy to create a PR here.
Issue Analytics
- State:
- Created 6 years ago
- Comments:12 (9 by maintainers)
Top GitHub Comments
@philmander Thanks for the jsbin links! I just checked with the official react docs and they actively discourage relying on reading
this.state
right after asetState
call. Instead they recommend using a callback function. For preact we encourage the same patterns, meaning it is always unsafe to rely onthis.state
immediately aftersetState
.The code currently found in master for
react-select
seems to have fixed this particular issue 🎉Just revisiting this issue. I created a couple of demos to illustrate the difference in case its not clear:
Preact: http://jsbin.com/taroyax/edit?html,js,console,output React: http://jsbin.com/vozuguj/edit?html,js,console,output