Rotate arrow indicator in React-select v2

2019-05-26 15:18发布

问题:

I'm using React Select v2 in my project with Styled Components and I need to be able to turn the arrow indicator upside down when the menu is open, which was supported in v1.

I kinda managed to do it by doing this:

   css`
     &.react-select__control--is-focused {
       & .react-select__indicators {
         & .react-select__dropdown-indicator {
           transform: rotate(180deg);
         }
       }
     }
   `;

Problem is that, if I press the arrow to open the menu and click on it again to close it, the arrow stays upside down because the select is still focused, which feels a bit weird in terms of UIX.

Is there a proper way to rotate it based on the state of the menu? I looked for something in the documentation but I couldn't find it.

Maybe I missed it, if someone could point me in the right direction, that'd be awesome!

Thanks!

回答1:

Technically you can use the style-in-JS props of the v2. Like the following example:

dropdownIndicator: (base, state) => ({
    ...base,
    transition: 'all .2s ease',
    transform: state.isFocused ? 'rotate(180deg)' : null
  })

It seems that the isFocused state isn't bind with the isMenuOpen state but with the real focus state of the container.

A solution is to set closeMenuOnSelect={false} so the user would have to click outside the select and your arrow will flip back.

Or you could change the className props using onMenuOpen and onMenuClose by adding a specific suffix to target your animation.

UPDATE

You can directly access the menuOpen props via the state so no need to manually add class like the following:

dropdownIndicator: (base, state) => ({
  ...base,
  transition: 'all .2s ease',
  transform: state.selectProps.menuIsOpen ? 'rotate(180deg)' : null
})

PLEASE NOTE THAT

In react-select v2.3 a control--menu-is-open has been added directly in the code.



回答2:

So, based on Laura's response, my solution was to use the onMenuClose and onMenuOpen to set the state of a property in my styled component.

const indicatorStyle = (props: StyledSelectProps & DropdownProps<{}>) => css`
  & .react-select__indicators {
      & .react-select__dropdown-indicator {
        transition: all .2s ease;
        transform: ${props.isOpen && "rotate(180deg)"};
      }
    }
`;

This function is called inside of my styled component's css.

And then in the component I call my styled component, I control the state:

export class Dropdown<TValue> extends React.Component<DropdownProps<TValue>> {
  public state = { isOpen: false };

  private onMenuOpen = () => this.setState({ isOpen: true });
  private onMenuClose = () => this.setState({ isOpen: false });

  public render() {
    const { ...props } = this.props;
    const { isOpen } = this.state;
    return (
      <StyledSelect {...props} isOpen={isOpen} onMenuOpen={this.onMenuOpen} onMenuClose={this.onMenuClose} />
    );
  }
}

A bit convoluted but it works for now.