Выбрать все / Отменить выбор всех опций в реакции-выбора
Возможно ли иметь опцию "Выбрать все / Отменить все" в реакции выбора? Это то, что встроено, или я должен сделать это сам?
5 ответов
В компании, где я работаю, мы сделали обертку вокруг react-select
с некоторыми дополнительными функциями и стилем. Одна из функций - Выбрать / Отменить выбор всех.
Вот ссылка на страницу, демонстрирующую компонент:
https://topia.design/components/multi-select/
Вот подход, который я использовал для реализации Select / Unselect All:
https://codesandbox.io/s/distracted-panini-8458i?file=/src/MultiSelect.js
import React, { useRef } from "react";
import ReactSelect from "react-select";
export const MultiSelect = props => {
// isOptionSelected sees previous props.value after onChange
const valueRef = useRef(props.value);
valueRef.current = props.value;
const selectAllOption = {
value: "<SELECT_ALL>",
label: "All Items"
};
const isSelectAllSelected = () =>
valueRef.current.length === props.options.length;
const isOptionSelected = option =>
valueRef.current.some(({ value }) => value === option.value) ||
isSelectAllSelected();
const getOptions = () => [selectAllOption, ...props.options];
const getValue = () =>
isSelectAllSelected() ? [selectAllOption] : props.value;
const onChange = (newValue, actionMeta) => {
const { action, option, removedValue } = actionMeta;
if (action === "select-option" && option.value === selectAllOption.value) {
props.onChange(props.options, actionMeta);
} else if (
(action === "deselect-option" &&
option.value === selectAllOption.value) ||
(action === "remove-value" &&
removedValue.value === selectAllOption.value)
) {
props.onChange([], actionMeta);
} else if (
actionMeta.action === "deselect-option" &&
isSelectAllSelected()
) {
props.onChange(
props.options.filter(({ value }) => value !== option.value),
actionMeta
);
} else {
props.onChange(newValue || [], actionMeta);
}
};
return (
<ReactSelect
isOptionSelected={isOptionSelected}
options={getOptions()}
value={getValue()}
onChange={onChange}
hideSelectedOptions={false}
closeMenuOnSelect={false}
isMulti
/>
);
};
Я вдохновился от метода Алекса, но я изменил какой-то раздел его кода. Вот пример, который я подготовил, если вам все еще нужно, вы можете проверить.
Вы можете легко сделать это следующим образом:
https://medium.com/@alex_escalante/react-select-alloptionoptions-with-a-single-click-1ebf5a33fe31
Для всех, кто заинтересован: Вдохновленный ответом @Mihhail Lapushkin, я создал типизированную версию, используя Next.js и Typescript. React-Select немного привередлив к SSR и SSG, поэтому ему требуются дополнительные идентификаторы. См. живую демонстрацию в CodeSandBox или наGitHub Многоразовый компонент выглядит примерно так:
import { ActionMeta, Options } from 'react-select';
import { Dispatch, SetStateAction, useRef } from "react";
import Select from 'react-select';
import makeAnimated from 'react-select/animated';
const animate = makeAnimated();
interface Props {
options: Option[]
selected: Option[],
setSelected: Dispatch<SetStateAction<any>>,
title: string,
hide?: boolean
}
export type Option = {
value: number | string;
label: string;
};
/* Advanced Dropdown Select Menu using React select
* Inspired by https://stackoverflow.com/a/61250357/20007391 and
* added typing.
*
* For more information about the react-select API, options, and
* customization, see https://react-select.com/home
*
* This component is just the select box. Options are passed in from
* the parent and the parent maintains the state of the object for
* callbacks and function handling. */
export default function MultiSelect(props: Props) {
// For component "memory"
const valueRef = useRef(props.selected);
valueRef.current = props.selected;
const selectAllOption = {value: "*", label:"Select All"};
const isSelectAllSelected = () => valueRef.current.length === props.options.length;
const isOptionSelected = (option: Option, selectValue: Options<Option>) =>
valueRef.current.some(({value}) => value === option.value) ||
isSelectAllSelected();
//const getOptions = () => isSelectAllSelected() ? [] : [selectAllOption, ...props.options];
const getOptions = () => [selectAllOption, ...props.options];
const getValue = () => isSelectAllSelected() ? [selectAllOption] : props.selected;
const handleSelect = (newValue: unknown, actionMeta: ActionMeta<unknown>) => {
const {action, option, removedValue} = actionMeta;
// Reassigning for typing. Unknown by default
const opt = option as Option;
const removed = removedValue as Option;
if (action === "select-option" && opt.value === selectAllOption.value) {
//console.log("new item selected");
props.setSelected(props.options);
} else if ((action === "deselect-option" && opt.value === selectAllOption.value) || (action === "remove-value" && removed.value === selectAllOption.value)) {
props.setSelected([]);
//console.log("all items removed");
} else if (actionMeta.action === "deselect-option" && isSelectAllSelected()) {
props.setSelected(
props.options.filter(({ value }) => value !== opt.value));
//console.log("new item removed");
} else {
props.setSelected(newValue || []);
//console.log("new item added");
}
//console.log(action, newValue, getValue());
}
return (
<Select
isOptionSelected={isOptionSelected}
closeMenuOnSelect={false}
defaultValue={getOptions()} // Should default to select all option
value={getValue()}
isMulti
//isSearchable
//components={animate}
placeholder={props.title}
options={getOptions()}
onChange={handleSelect}
hideSelectedOptions={props.hide ?? false}
instanceId={props.title}
id={props.title}
/>
)
}
Единственная липкая вещь связана с определением того, как выглядят ваши варианты. Вы можете отформатировать свои параметры, чтобы они были одинаковыми, или определить общий и настроить защиту типа для проверки неизвестных значений (по умолчанию для ActionMeta<> и newValue в реакции-выборе). Ведение журнала консоли добавлено для удобства и демонстрационных целей.
Решение для react-select@5.2.0 (изящно обеспечивает опцию «выбрать все») (удалить части TypeScript для запуска в чистом jS)
const MultiSelect: React.FC<MultiSelectProps> = ({
options,
defaultValue,
onChange,
}) => {
const selectAllOption = { label: 'select all', value: '*' };
const initialVisibleOptions =
options.length === defaultValue?.length
? options
: [...options, selectAllOption];
const [availableOptions, setAvailableOptions] = useState<Option[]>(
initialVisibleOptions,
);
const [selectedValues, setSelectedValues] = useState<readonly Option[]>(
defaultValue,
);
const relayOnChange = (newSelectedOptions: readonly Option[]) => {
const selectAllIsSelected = !!newSelectedOptions.find(
o => o.value === selectAllOption.value,
);
const newComponentState = selectAllIsSelected
? {
selectedValues: options,
availableOptions: [],
}
: {
selectedValues: newSelectedOptions,
availableOptions: initialVisibleOptions,
};
setSelectedValues(newComponentState.selectedValues);
setAvailableOptions(newComponentState.availableOptions);
onChange(newComponentState.selectedValues);
};
return (
<Select
isMulti
options={availableOptions}
value={selectedValues}
defaultValue={selectedValues}
onChange={relayOnChange}
/>
);
};