admin管理员组文章数量:1402315
I'm using react-select and have made a few customizations to it. I tried to modify the ValueContainer and the SelectContainer ponents which cause some issues. The dropdown wont close when I click outside of it after having selected a value. I'm wondering if anyone can see what I have done wrong here?
I'm assuming that the dropdown wont close because the onBlur event for the Input doesn't trigger. E.g the input doesn't regain its focus after i click a value with closeMenuOnSelect
= true. I am also assuming that this is because react-select cant find the input for some reason because I've modified the structure. Might be missing some refs or something but I can't understand where to get them and where to put them. Anyone know?
Here is my custom Select ponent:
const ReactSelect: React.FC<ReactSelectProps> = ({
backgroundColor,
isSearchable = false,
placeholder = '',
size,
grow,
className,
required,
label,
variant = 'underline',
dataTestId,
...props
}) => {
const filterConfig = {
ignoreCase: true,
ignoreAccents: true,
matchFromStart: false,
stringify: (option: any) => `${option.label}`,
trim: true,
};
const [showIsRequired, setShowIsRequired] = useState(
!props.defaultValue && required
);
const handleOnChange = (newValue: any, actionMeta: ActionMeta<any>) => {
setShowIsRequired(!newValue && required);
if (props.onChange) {
props.onChange(newValue, actionMeta);
}
};
const properties = {
...props,
className: className,
onChange: handleOnChange,
isSearchable: isSearchable,
styles: customStyles(size, backgroundColor, grow, showIsRequired, variant),
menuPlacement: 'auto' as MenuPlacement,
placeholder: placeholder,
noOptionsMessage: () => t`Ingen elementer funnet`,
loadingMessage: () => t`Laster...`,
filterOption: createFilter(filterConfig),
closeMenuOnSelect: !props.isMulti,
hideSelectedOptions: false,
ponents: {
ValueContainer: (inputProps: ValueContainerProps) => (
<ValueContainer required={required} label={label} {...inputProps} />
),
Option,
SelectContainer: (containerProps: ContainerProps) => (
<SelectContainer dataTestId={dataTestId} {...containerProps} />
),
},
};
if ('value' in props) {
return <StateManagedSelect {...properties} />;
}
return <Select {...properties} />;
};
And here are my custom <SelectContainer>
and <ValueContainer>
:
const ValueContainer = ({ children, label, required, ...rest }: any) => {
const labelFloatingStyle = {
top: '0.30rem',
left: '0.6rem',
transform: 'translate(0, 0) scale(1)',
fontSize: '0.75rem',
color: 'hsl(236, 91%, 9%)',
fontWeight: '500',
};
const labelStyle = {
top: '50%',
left: '0.75rem',
transform: 'translate(0, -50%) scale(1)',
fontSize: '1rem',
color: 'hsl(0, 0%, 45%)',
};
const requiredDotStyles = {
color: 'hsl(35, 100%, 43%)',
fontSize: '1.5rem',
display: rest.hasValue ? 'none' : 'inline',
};
const getLabelStyles = () =>
rest.hasValue || rest.isFocused ? labelFloatingStyle : labelStyle;
return (
<ponents.ValueContainer {...rest}>
{children}
{label && (
<label
style={{
position: 'absolute',
transformOrigin: 'left bottom',
transition: 'all 0.2s',
display: 'flex',
alignItems: 'center',
...getLabelStyles(),
}}
>
{label} {required && <span style={requiredDotStyles}>*</span>}
</label>
)}
</ponents.ValueContainer>
);
};
const SelectContainer = ({ dataTestId, ...rest }: any) => (
<div data-test-id={dataTestId}>
<ponents.SelectContainer {...rest} />
</div>
);
I'm using react-select and have made a few customizations to it. I tried to modify the ValueContainer and the SelectContainer ponents which cause some issues. The dropdown wont close when I click outside of it after having selected a value. I'm wondering if anyone can see what I have done wrong here?
I'm assuming that the dropdown wont close because the onBlur event for the Input doesn't trigger. E.g the input doesn't regain its focus after i click a value with closeMenuOnSelect
= true. I am also assuming that this is because react-select cant find the input for some reason because I've modified the structure. Might be missing some refs or something but I can't understand where to get them and where to put them. Anyone know?
Here is my custom Select ponent:
const ReactSelect: React.FC<ReactSelectProps> = ({
backgroundColor,
isSearchable = false,
placeholder = '',
size,
grow,
className,
required,
label,
variant = 'underline',
dataTestId,
...props
}) => {
const filterConfig = {
ignoreCase: true,
ignoreAccents: true,
matchFromStart: false,
stringify: (option: any) => `${option.label}`,
trim: true,
};
const [showIsRequired, setShowIsRequired] = useState(
!props.defaultValue && required
);
const handleOnChange = (newValue: any, actionMeta: ActionMeta<any>) => {
setShowIsRequired(!newValue && required);
if (props.onChange) {
props.onChange(newValue, actionMeta);
}
};
const properties = {
...props,
className: className,
onChange: handleOnChange,
isSearchable: isSearchable,
styles: customStyles(size, backgroundColor, grow, showIsRequired, variant),
menuPlacement: 'auto' as MenuPlacement,
placeholder: placeholder,
noOptionsMessage: () => t`Ingen elementer funnet`,
loadingMessage: () => t`Laster...`,
filterOption: createFilter(filterConfig),
closeMenuOnSelect: !props.isMulti,
hideSelectedOptions: false,
ponents: {
ValueContainer: (inputProps: ValueContainerProps) => (
<ValueContainer required={required} label={label} {...inputProps} />
),
Option,
SelectContainer: (containerProps: ContainerProps) => (
<SelectContainer dataTestId={dataTestId} {...containerProps} />
),
},
};
if ('value' in props) {
return <StateManagedSelect {...properties} />;
}
return <Select {...properties} />;
};
And here are my custom <SelectContainer>
and <ValueContainer>
:
const ValueContainer = ({ children, label, required, ...rest }: any) => {
const labelFloatingStyle = {
top: '0.30rem',
left: '0.6rem',
transform: 'translate(0, 0) scale(1)',
fontSize: '0.75rem',
color: 'hsl(236, 91%, 9%)',
fontWeight: '500',
};
const labelStyle = {
top: '50%',
left: '0.75rem',
transform: 'translate(0, -50%) scale(1)',
fontSize: '1rem',
color: 'hsl(0, 0%, 45%)',
};
const requiredDotStyles = {
color: 'hsl(35, 100%, 43%)',
fontSize: '1.5rem',
display: rest.hasValue ? 'none' : 'inline',
};
const getLabelStyles = () =>
rest.hasValue || rest.isFocused ? labelFloatingStyle : labelStyle;
return (
<ponents.ValueContainer {...rest}>
{children}
{label && (
<label
style={{
position: 'absolute',
transformOrigin: 'left bottom',
transition: 'all 0.2s',
display: 'flex',
alignItems: 'center',
...getLabelStyles(),
}}
>
{label} {required && <span style={requiredDotStyles}>*</span>}
</label>
)}
</ponents.ValueContainer>
);
};
const SelectContainer = ({ dataTestId, ...rest }: any) => (
<div data-test-id={dataTestId}>
<ponents.SelectContainer {...rest} />
</div>
);
Share
Improve this question
asked Feb 15, 2022 at 14:51
mTvmTv
1,3964 gold badges22 silver badges41 bronze badges
2
- were you able to fixed this? Got the same issue too. – ruin3936 Commented Mar 9, 2022 at 9:00
- Yes. Check out the answer i posted – mTv Commented Mar 9, 2022 at 10:41
4 Answers
Reset to default 2The problem was with how I sent in the custom ponents. You shouldn't pass the props there. I think that might ruin some of the important ReactSelect props like refs and such.
Send the ponents in like this instead. Define your custom properties in this object as well:
const properties = {
...props,
className: className,
onChange: handleOnChange,
isSearchable: isSearchable,
styles: customStyles(size, backgroundColor, grow, showIsRequired, variant),
menuPlacement: 'auto' as MenuPlacement,
placeholder: placeholder,
noOptionsMessage: () => t`Ingen elementer funnet`,
loadingMessage: () => t`Laster...`,
filterOption: createFilter(filterConfig),
closeMenuOnSelect: !props.isMulti,
hideSelectedOptions: false,
label: label,
required: required,
dataTestId: dataTestId,
ponents: {
ValueContainer,
Option,
SelectContainer,
},
};
if ('value' in props) {
return <StateManagedSelect {...properties} />;
}
return <Select {...properties} />;
Then you can get these custom props in the custom ponent using props.selectProps
like this:
const ValueContainer = ({ children, ...rest }: any) => {
const { label, required } = rest.selectProps; // HERE
const labelFloatingStyle = {
top: '0.20rem',
left: '0.6rem',
transform: 'translate(0, 0) scale(1)',
fontSize: '0.75rem',
color: 'hsl(236, 91%, 9%)',
fontWeight: '500',
};
const labelStyle = {
top: '50%',
left: '0.75rem',
transform: 'translate(0, -50%) scale(1)',
fontSize: '1rem',
color: 'hsl(0, 0%, 45%)',
};
const requiredDotStyles = {
color: 'hsl(35, 100%, 43%)',
fontSize: '1.5rem',
display: rest.hasValue ? 'none' : 'inline',
};
const getLabelStyles = () =>
rest.hasValue || rest.isFocused ? labelFloatingStyle : labelStyle;
return (
<ponents.ValueContainer {...rest}>
{children}
{label && (
<label
style={{
position: 'absolute',
transformOrigin: 'left bottom',
transition: 'all 0.2s',
display: 'flex',
alignItems: 'center',
cursor: 'pointer',
...getLabelStyles(),
}}
>
{label} {required && <span style={requiredDotStyles}>*</span>}
</label>
)}
</ponents.ValueContainer>
);
};
Here is a solution that is taken from the accepted one which is more generic.
The code that was breaking for me when trying to click outside:
<Select
isMulti
name={labelText}
placeholder={placeholder}
defaultValue={defaultValues}
options={options}
onChange={handleChange}
onMenuClose={onMenuClose}
ponents={{
Option,
ValueContainer: (props) => (
<ValueContainer {...props} maxItems={maxItems} label={label} />
)
}}
/>
so right inside the ponent, I created a valueContainerProps object and passed the props to the select ponent directly by spreading them. These props are accessible inside the selectProps param of the props received by ValueContainer
// ....
const valueContainerProps = {
maxItems,
label
};
return (
<>
<Select
isMulti
name={label}
placeholder={placeholder}
defaultValue={defaultValues}
options={options}
onChange={handleChange}
onMenuClose={onMenuClose}
ponents={{
Option,
ValueContainer
}}
{ ...valueContainerProps }
/>
</>)
and then -
const ValueContainer = ({ children, ...props }: ValueContainerProps<LabelAndValue, true>) => {
const { maxItems = 2, label = 'options' } = props.selectProps as any;
// ... rest of the code
hope this helps.
The problem is in the ValueContainer, since the focus is lost from our selector, the easiest way to solve this problem is to add focus to the ValueContainer
const refSelect = useRef<any>(null);
const ValueContainer: React.FC<CustomValueContainerProps> = ({
...props
}) => {
let [values, input] = props.children;
if (refSelect?.current) {
refSelect.current.focus();
}
return (
<ponents.ValueContainer {...props}>
{input}
{values}
</ponents.ValueContainer>
);
};
<CustomSelect
value={value}
classNamePrefix={'Select'}
options={options}
onChange={handleChange}
closeMenuOnSelect={closeMenuOnSelect}
ref={refSelect}
ponents={{
ValueContainer,
}}
/>
As per Dmytry's answer, the only thing I would change is changing when the select should actually have the focus. Otherwise, the focus will always be hijacked by your ponent on any page you use it.
const refSelect = useRef<any>(null);
const ValueContainer: React.FC<CustomValueContainerProps> = ({
...props
}) => {
let [values, input] = props.children;
//only gain focus if the menu is open. This also helps with cross click close when closeMenuOnSelect is false
if (refSelect?.current && props.selectProps.menuIsOpen) {
refSelect.current.focus();
}
return (
<ponents.ValueContainer {...props}>
{input}
{values}
</ponents.ValueContainer>
);
};
<CustomSelect
value={value}
classNamePrefix={'Select'}
options={options}
onChange={handleChange}
closeMenuOnSelect={closeMenuOnSelect}
ref={refSelect}
ponents={{
ValueContainer,
}}
/>
本文标签: javascriptreactselect wont close when clicking outsideStack Overflow
版权声明:本文标题:javascript - react-select wont close when clicking outside - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744351307a2602066.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论