Интеграция Slim Sel ect с React требует понимания различий между
императивной библиотекой управления DOM и декларативной моделью React.
Slim Sel ect напрямую работает с DOM-элементами
<select>, тогда как React управляет состоянием через
виртуальный DOM, что приводит к необходимости аккуратного связывания
жизненных циклов компонента и экземпляра Slim Select.
Библиотека устанавливается через пакетный менеджер:
npm install slim-select
Подключение стилей обязательно, так как Slim Select не предоставляет визуального оформления без CSS:
import 'slim-select/styles';
При использовании сборщиков (Vite, Webpack, CRA) импорт стилей обрабатывается автоматически.
Основная идея заключается в инициализации Slim Select внутри
useEffect и привязке его к ref:
import { useEffect, useRef } fr om 'react';
import SlimSelect fr om 'slim-select';
import 'slim-select/styles';
export default function SelectComponent() {
const selectRef = useRef(null);
const slimRef = useRef(null);
useEffect(() => {
if (!selectRef.current) return;
slimRef.current = new SlimSelect({
select: selectRef.current
});
return () => {
if (slimRef.current) {
slimRef.current.destroy();
slimRef.current = null;
}
};
}, []);
return (
<sel ect ref={selectRef}>
<option value="1">Первый</option>
<option value="2">Второй</option>
</select>
);
}
Ключевой момент — обязательное уничтожение экземпляра через
destroy() при размонтировании компонента, иначе возникает
утечка DOM-обработчиков.
React-архитектура предполагает хранение состояния выбора в
useState, тогда Slim Sel ect используется как внешний
синхронизатор UI.
import { useEffect, useRef, useState } fr om 'react';
import SlimSelect fr om 'slim-select';
export default function ControlledSelect() {
const selectRef = useRef(null);
const slimRef = useRef(null);
const [value, setValue] = useState('2');
useEffect(() => {
slimRef.current = new SlimSelect({
select: selectRef.current,
events: {
afterChange: (newValue) => {
setValue(newValue[0]?.value || '');
}
}
});
return () => slimRef.current?.destroy();
}, []);
useEffect(() => {
if (slimRef.current) {
slimRef.current.setSelected(value);
}
}, [value]);
return (
<sel ect ref={selectRef} defaultValue={value}>
<option value="1">Первый</option>
<option value="2">Второй</option>
<option value="3">Третий</option>
</select>
);
}
afterChange служит мостом между Slim Select и React
statesetSelected() используется для обратной
синхронизацииdefaultValue предотвращает конфликт начальной
отрисовкиSlim Select не отслеживает изменения React-дерева автоматически, поэтому требуется ручное обновление данных.
useEffect(() => {
if (!slimRef.current) return;
slimRef.current.setData([
{ text: 'Один', value: '1' },
{ text: 'Два', value: '2' },
{ text: 'Три', value: '3' }
]);
}, [options]);
Где options — массив состояния React.
При работе с API важно избегать гонок состояний и повторной инициализации Slim Select.
useEffect(() => {
let active = true;
async function load() {
const res = await fetch('/api/options');
const data = await res.json();
if (!active || !slimRef.current) return;
slimRef.current.setData(
data.map(item => ({
text: item.name,
value: item.id
}))
);
}
load();
return () => {
active = false;
};
}, []);
Slim Select поддерживает multiple, но React требует
корректной синхронизации массива значений.
const [values, setValues] = useState(['1', '3']);
useEffect(() => {
slimRef.current = new SlimSelect({
select: selectRef.current,
events: {
afterChange: (selected) => {
setValues(selected.map(v => v.value));
}
}
});
return () => slimRef.current?.destroy();
}, []);
useEffect(() => {
slimRef.current?.setSelected(values);
}, [values]);
В режиме разработки React 18 может дважды вызывать
useEffect, что приводит к двойной инициализации Slim
Select.
Решение заключается в строгом контроле очистки:
useEffect(() => {
if (slimRef.current) {
slimRef.current.destroy();
}
slimRef.current = new SlimSelect({
select: selectRef.current
});
return () => {
slimRef.current?.destroy();
slimRef.current = null;
};
}, []);
При условной отрисовке важно учитывать момент появления DOM-элемента:
useEffect(() => {
if (!enabled) return;
if (!selectRef.current) return;
slimRef.current = new SlimSelect({
select: selectRef.current
});
return () => slimRef.current?.destroy();
}, [enabled]);
Slim Select часто используется в связке с формами:
import { useForm } fr om 'react-hook-form';
function Form() {
const { register, setValue } = useForm();
const selectRef = useRef(null);
useEffect(() => {
const slim = new SlimSelect({
select: selectRef.current,
events: {
afterChange: (selected) => {
setValue('field', selected[0]?.value);
}
}
});
return () => slim.destroy();
}, []);
return (
<form>
<sel ect {...register('field')} ref={selectRef}>
<option value="a">A</option>
<option value="b">B</option>
</select>
</form>
);
}
Slim Select зависит от window и DOM, поэтому требует
клиентской инициализации.
import dynamic fr om 'next/dynamic';
const Select = dynamic(() => import('./SelectComponent'), {
ssr: false
});
Инициализация выполняется только на клиенте, иначе возникает ошибка гидратации.
Slim Select может изменять UI независимо от React, что иногда приводит к рассинхронизации. В таких случаях приоритет отдаётся React state как источнику истины, а Slim Select рассматривается как слой отображения.
Правильная очистка предотвращает накопление обработчиков событий:
return () => {
if (slimRef.current) {
slimRef.current.destroy();
slimRef.current = null;
}
};
Дополнительно важно сбрасывать ссылки при условной отрисовке компонентов, чтобы исключить повторное использование устаревших DOM-узлов.
destroy()null ref при асинхронной
загрузкеАрхитектурно взаимодействие строится по схеме:
<select>Такое разделение минимизирует конфликт между виртуальным DOM и прямыми DOM-манипуляциями