React-select: Disable Chrome autofill allowing autoComplete="new-password" to be passed into the Input component properties

Created on 3 Apr 2019  路  10Comments  路  Source: JedWatson/react-select

Currently, the default Input component is forcefully setting autoComplete="off" which doesn't disable Chrome's autofill functionality on latest browser versions. For achieving that, we need to pass autoComplete="new-password".

The problem is that the value is hardcoded and can't be configured unless we implement our own Input component and configure this property properly there.

It's hardcoded here: https://github.com/JedWatson/react-select/blob/ba76246a92fe9371b5d4f8795d30119b045dcaba/src/Select.js#L1401

And it gets passed to the default Input component here: https://github.com/JedWatson/react-select/blob/ba76246a92fe9371b5d4f8795d30119b045dcaba/src/components/Input.js#L53

As you can see, there's no way of configuring that property manually.

issuneeds-review issureviewed

Most helpful comment

As you can see, there's no way of configuring that property manually.

@rdsedmundo Yes, there is a possibility to configure it. It is hard coded as a prop to the Input component, that is true. But this library offers in its current version the possibility to overwrite its internal components using its components framework.

You just have to overwrite the Input component and overwrite the prop being passed to the component.

import Select, { components } from 'react-select';

const Input = ({ autoComplete, ...props }) => <components.Input {...props} autoComplete="new-password" />;

<Select
    { ... }
    components={{
        Input
    }}
/>

We have resorted to forking the project, but it would be nice if this were supported natively.

@kylehurt-rkv Here is your native support, without forking the repository.

All 10 comments

I agree that this would be a welcome change. I have been fighting with this for the last couple of days. It is a dealbreaker for our app because the autocomplete control overlays the react-select menu. We have resorted to forking the project, but it would be nice if this were supported natively.

As you can see, there's no way of configuring that property manually.

@rdsedmundo Yes, there is a possibility to configure it. It is hard coded as a prop to the Input component, that is true. But this library offers in its current version the possibility to overwrite its internal components using its components framework.

You just have to overwrite the Input component and overwrite the prop being passed to the component.

import Select, { components } from 'react-select';

const Input = ({ autoComplete, ...props }) => <components.Input {...props} autoComplete="new-password" />;

<Select
    { ... }
    components={{
        Input
    }}
/>

We have resorted to forking the project, but it would be nice if this were supported natively.

@kylehurt-rkv Here is your native support, without forking the repository.

That's a smarter solution than what I did. I knew I could just overwrite the Input, that's what I did for fixing it, but I just copied its source code originally and pasted on my codebase. I haven't thought about the possibility of just importing it from the package.

I still can see value of having it configurable though.

@Rall3n We actually tried that exact solution, but whenever the page would try to load, Chrome would get of memory errors and crash. We are supplying several other custom components into the Select component, so it may be possible that something we did on one of the other custom components was conflicting with custom input component. Or maybe we are doing something in a non-standard way.
Here is our code with the addition of the custom input component. Chrome throws an out of memory error and never loads the page. I'm guessing there is a circular reference somewhere.
`
import React from 'react';
import PropTypes from 'prop-types';
import RSelect from 'react-select';
import { withStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import NoSsr from '@material-ui/core/NoSsr';
import TextField from '@material-ui/core/TextField';
import Paper from '@material-ui/core/Paper';
import MenuItem from '@material-ui/core/MenuItem';
import FormControl from '@material-ui/core/FormControl/FormControl';
import FormHelperText from '@material-ui/core/FormHelperText/FormHelperText';
import { remove } from 'react-icons-kit/fa/remove';
import { chevronDown } from 'react-icons-kit/fa/chevronDown';
import { Icon } from 'react-icons-kit';
import Tooltip from '@material-ui/core/Tooltip';
import { isErrorState } from './reduxFormHelper';

const styles = theme => ({
root: {
flexGrow: 1,
},
input: {
display: 'flex',
paddingTop: 2,
paddingBottom: 3,
},
valueContainer: {
display: 'flex',
flexWrap: 'wrap',
flex: 1,
alignItems: 'center',
},
noOptionsMessage: {
padding: ${theme.spacing.unit}px ${theme.spacing.unit * 2}px,
},
singleValue: {
fontSize: 16,
},
placeholder: {
position: 'absolute',
left: 2,
fontSize: 16,
},
paper: {
position: 'absolute',
zIndex: 1,
marginTop: 0,
paddingTop: 0,
left: 0,
right: 0,
},
});

class Select extends React.Component {
findOption() {
const { input, options } = this.props;
const value = input.value._id || input.value;
return options.find(option => option.value === value);
}

render() {
const {
classes,
theme,
fullWidth,
label,
id,
placeholder,
required,
meta: { touched, error },
options,
isReadOnly,
input,
} = this.props;

function NoOptionsMessage(props) {
  return (
    <Typography
      color="textSecondary"
      className={props.selectProps.classes.noOptionsMessage}
      {...props.innerProps}
    >
      {props.children}
    </Typography>
  );
}

function inputComponent({ inputRef, ...props }) {
  return <div ref={inputRef} {...props} />;
}

const Control = props => {
  return (
    <div className="app-input-group">
      <FormControl
        fullWidth={fullWidth}
        error={isErrorState(touched, error)}
      >
        <TextField
          error={isErrorState(touched, error)}
          label={label}
          id={id}
          required={required}
          InputProps={{
            inputComponent,
            inputProps: {
              className: props.selectProps.classes.input,
              inputRef: props.innerRef,
              children: props.children,
              ...props.innerProps,
            },
          }}
          {...props.selectProps.textFieldProps}
        />
        <FormHelperText>{touched && error}</FormHelperText>
      </FormControl>
    </div>
  );
};

function Option(props) {
  return (
    <MenuItem
      buttonRef={props.innerRef}
      selected={props.isFocused}
      component="div"
      style={{
        fontWeight: props.isSelected ? 500 : 400,
      }}
      {...props.innerProps}
    >
      {props.children}
    </MenuItem>
  );
}

function Placeholder(props) {
  return (
    <Typography
      color="textSecondary"
      className={props.selectProps.classes.placeholder}
      {...props.innerProps}
    >
      {props.children}
    </Typography>
  );
}

function SingleValue(props) {
  return (
    <Typography
      className={props.selectProps.classes.singleValue}
      {...props.innerProps}
    >
      {props.children}
    </Typography>
  );
}

function ValueContainer(props) {
  return (
    <div className={props.selectProps.classes.valueContainer}>
      {props.children}
    </div>
  );
}

function Menu(props) {
  return isReadOnly ? null : (
    <Paper
      square
      className={props.selectProps.classes.paper}
      {...props.innerProps}
    >
      {props.children}
    </Paper>
  );
}

function ClearIndicator(props) {
  const {
    innerProps: { ref, ...restInnerProps },
  } = props;
  return (
    <div
      {...restInnerProps}
      ref={ref}
      className="app-select-clear-ind-container"
    >
      <Tooltip title="Clear">
        <Icon icon={remove} className="app-select-clear-ind" />
      </Tooltip>
    </div>
  );
}

function DropdownIndicator(props) {
  return (
    <div className="app-select-dd-ind-container">
      <Icon className="app-select-dd-ind" icon={chevronDown} />
    </div>
  );
}

function IndicatorsContainer(props) {
  return isReadOnly ? null : (
    <div className="app-select-ind-container">{props.children}</div>
  );
}

function IndicatorSeparator(props) {
  return <span className="app-select-ind-sep" {...props.innerProps} />;
}

const Input = ({ autoComplete, ...props }) => (
  <components.Input {...props} autoComplete="new-password" />
);

const components = {
  Control,
  Menu,
  NoOptionsMessage,
  Option,
  Placeholder,
  SingleValue,
  ValueContainer,
  DropdownIndicator,
  ClearIndicator,
  IndicatorsContainer,
  IndicatorSeparator,
  Input,
};

const selectStyles = {
  input: base => ({
    ...base,
    color: theme.palette.text.primary,
    '& input': {
      font: 'inherit',
    },
  }),
};

return (
  <div className={classes.root}>
    <NoSsr>
      <RSelect
        isClearable
        classes={classes}
        styles={selectStyles}
        isDisabled={isReadOnly}
        textFieldProps={{
          InputLabelProps: {
            shrink: true,
          },
        }}
        options={options}
        components={components}
        value={this.findOption()}
        onChange={
          !isReadOnly
            ? option => input.onChange(option ? option.value : null)
            : null
        }
        placeholder={placeholder}
        openMenuOnClick={!isReadOnly}
      />
    </NoSsr>
  </div>
);

}
}

Select.defaultProps = {
fullWidth: true,
};

Select.propTypes = {
classes: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired,
};

export default withStyles(styles, { withTheme: true })(Select);

`

image
This the debugger window Chrome throws up.

@kylehurt-rkv If I麓m seeing this correctly, you are creating the custom components inside your render function? This could be the source of your problem.

Creating the components inside the render causes a complete rerender, because with each call of render the variables are recreated. Try declaring the components outside the render function (I would recommend outside of the class if you can, else inside the class).

@Rall3n Turns out I was right. I WAS doing something in a non-standard way. Moving the custom components outside of the class took care of the issue I was having with Chrome crashing. Makes sense now that I know the answer. Thank you very much for your insight.

Seems like there's a good reason to have this autoComplete configurable, as based on the age and responses of https://github.com/JedWatson/react-select/pull/2395

I have a similar problem in #4006 : I am looking for a way to make sure that the Dashlane autofill icon does not get active in my AsyncComponent which results in a crash of the Chrome browser. Dashlane support hasn't been helpful so far, so if anybody of you has an idea on how to lock Dashlane out of my AsyncComponent, i'd be happy to hear!

This seems somewhat unnecessary given the existing component api as Rall3n has already mentioned.

Here you can already pass in an autoComplete prop to the Select and have it rendered in a custom Input component.

Note: This can be applied to any prop you want to apply to the Input

Working demo: codesandbox

const Input = (props) => {
  const { autoComplete = props.autoComplete } = props.selectProps;
  return <components.Input {...props} autoComplete={autoComplete} />;
};

const MySelect = (props) => (
   <Select components={{ Input }} autoComplete="new-password" options={options} />
);
Was this page helpful?
0 / 5 - 0 ratings

Related issues

pablote picture pablote  路  3Comments

coder-guy22296 picture coder-guy22296  路  3Comments

pashap picture pashap  路  3Comments

MalcolmDwyer picture MalcolmDwyer  路  3Comments

geraldfullam picture geraldfullam  路  3Comments