React JSX: Как выбрать "selected" для выбранного элемента <select>
Проблема с атрибутом selected в компоненте React для
В компоненте React для меню <select>
, мне необходимо установить атрибут selected
на элементе <option>
, который соответствует состоянию приложения.
В методе render()
состояние optionState
передается от владельца состояния в компонент SortMenu. Значения опций передаются через props
из JSON.
render: function() {
var options = [],
optionState = this.props.optionState;
this.props.options.forEach(function(option) {
var selected = (optionState === option.value) ? ' selected' : '';
options.push(
<option value={option.value}{selected}>{option.label}</option>
);
});
// передаем {options} в JSX меню select
}
Однако это вызывает синтаксическую ошибку при компиляции JSX.
Использование следующего варианта устраняет синтаксическую ошибку, но, очевидно, не решает проблему:
var selected = (optionState === option.value) ? 'selected' : 'false';
<option value={option.value} selected={selected}>{option.label}</option>
Я также пробовал следующее:
var selected = (optionState === option.value) ? true : false;
<option value={option.value} {selected ? 'selected' : ''}>{option.label}</option>
Существуют ли рекомендуемые способы решения данной проблемы?
5 ответ(ов)
Вы можете сделать то, о чем React предупреждает, когда вы пытаетесь установить свойство selected
у <option>
:
Используйте свойства
defaultValue
илиvalue
у<select>
, вместо того чтобы устанавливатьselected
у<option>
.
Таким образом, вы можете использовать options.value
в качестве defaultValue
вашего <select>
.
Вот полное решение, которое включает в себя лучший ответ и комментарии ниже него (что может помочь тем, кто пытается собрать всё воедино):
ОБНОВЛЕНИЕ ДЛЯ ES6 (2019) - использование стрелочных функций и деструктуризация объектов
В главном компоненте:
class ReactMain extends React.Component {
constructor(props) {
super(props);
this.state = { fruit: props.item.fruit };
}
handleChange = (event) => {
this.setState({ [event.target.name]: event.target.value });
}
saveItem = () => {
const item = {};
item.fruit = this.state.fruit;
// делаем больше с объектом item по необходимости (например, сохранить в базе данных)
}
render() {
return (
<ReactExample name="fruit" value={this.state.fruit} handleChange={this.handleChange} />
)
}
}
Включённый компонент (который теперь является статическим функциональным):
export const ReactExample = ({ name, value, handleChange }) => (
<select name={name} value={value} onChange={handleChange}>
<option value="A">Яблоко</option>
<option value="B">Банан</option>
<option value="C">Клюква</option>
</select>
)
ПРЕДЫДУЩИЙ ОТВЕТ (с использованием bind):
В главном компоненте:
class ReactMain extends React.Component {
constructor(props) {
super(props);
// связываем один раз здесь, лучше, чем множество раз в render
this.handleChange = this.handleChange.bind(this);
this.state = { fruit: props.item.fruit };
}
handleChange(event) {
this.setState({ [event.target.name]: event.target.value });
}
saveItem() {
const item = {};
item.fruit = this.state.fruit;
// делаем больше с объектом item по необходимости (например, сохранить в базе данных)
}
render() {
return (
<ReactExample name="fruit" value={this.state.fruit} handleChange={this.handleChange} />
)
}
}
Включённый компонент (который теперь является статическим функциональным):
export const ReactExample = (props) => (
<select name={props.name} value={props.value} onChange={props.handleChange}>
<option value="A">Яблоко</option>
<option value="B">Банан</option>
<option value="C">Клюква</option>
</select>
)
Главный компонент поддерживает выбранное значение для фрукта (в состоянии), включённый компонент отображает элемент выбора и обновления передаются обратно в главный компонент для обновления его состояния (что затем возвращается в включённый компонент для изменения выбранного значения).
Обратите внимание на использование пропса name
, который позволяет объявлять один метод handleChange
для других полей на той же форме независимо от их типа.
Основная идея - Управляемый компонент
Вы хотите настроить "Управляемый компонент". Для этого вам необходимо установить значение на элементе, а также обработать событие изменения, чтобы обновить значение.
https://reactjs.org/docs/forms.html#controlled-components
Примеры
https://codepen.io/codyswartz/pen/QWqYNrY
Простой пример функционального компонента Select
Этот пример также включает значение по умолчанию и затем делает его серым.
const defaultSelectValue = "Выберите фрукт"
const SelectExample = () => {
const [selected, setSelected] = useState(defaultSelectValue)
return (
<>
<label htmlFor="fruits">Фрукты</label>{' '}
<select
id="fruits"
name="fruits"
defaultValue={selected}
style={{ color: selected === defaultSelectValue ? "gray" : "black" }}
onChange={e => setSelected(e.target.value)}
>
<option>{defaultSelectValue}</option>
<option>Банан</option>
<option>Яблоко</option>
<option>Апельсин</option>
</select>
<h2>Выбрано: {selected}</h2>
</>
)
}
// Использование
<SelectExample />
Динамический повторно используемый пример с умолчанием
Этот пример принимает коллекцию строк, используя первую как значение по умолчанию.
const SelectExample = ({ name, items }) => {
const defaultSelectValue = items[0]
const [selected, setSelected] = useState(defaultSelectValue)
return (
<>
<label htmlFor={name}>{name}</label>{' '}
<select
id={name}
name={name}
defaultValue={selected}
style={{ color: selected === defaultSelectValue ? "gray" : "black" }}
onChange={e => setSelected(e.target.value)}
>
{items.map(item => (
<option key={item} value={item}>
{item}
</option>
))}
</select>
<h2>Выбрано: {selected}</h2>
</>
)
}
// Использование
<SelectExample
name="фрукты"
items={['Выберите фрукт', 'Банан', 'Яблоко', 'Апельсин']}
/>
Просто добавьте в качестве первого варианта в ваш тег <select>
следующий код:
<option disabled hidden value=''></option>
Это станет значением по умолчанию, и когда вы выберете действительный вариант, он будет установлен в ваше состояние.
В вашем коде есть несколько вариантов реализации компонента DropDown
, который отвечает за отображение выпадающего списка. Давайте рассмотрим их подробнее.
Вариант 1:
var DropDown = React.createClass({ render: function () { var items = this.props.data; return ( <select value={this.props.Selected}> { items.map(function (item) { return <option value={item.Name}>{item.Name}</option>; }) } </select> ); } });
В этом варианте используется атрибут
value
у элементаselect
, который устанавливается в значение изprops.Selected
. Это самый распространенный способ управления состоянием выбранного элемента для контролируемых компонентов в React.Вариант 2:
var DropDown = React.createClass({ render: function () { var items = this.props.data; return ( <select> { items.map(function (item) { return <option value={item.Name} selected={selectedItem == item.Name}>{item.Name}</option>; }) } </select> ); } });
Здесь используется атрибут
selected
для каждой опции, однако это не самый лучший подход, так как он не соответствует концепции контролируемых компонентов в React. Лучше использовать атрибутvalue
на уровнеselect
, как в первом варианте.Вариант 3:
var DropDown = React.createClass({ render: function () { var items = this.props.data; return ( <select> { items.map(function (item) { if (selectedItem == item.Name) return <option value={item.Name} selected>{item.Name}</option>; else return <option value={item.Name}>{item.Name}</option>; }) } </select> ); } });
Этот подход тоже не является лучшим способом, так как вы используете условную логику внутри метода
map
для определения того, какая опция должна быть выбранной. Это может привести к нестабильному поведению и сложности в управлении состоянием.
Рекомендация
Наиболее корректный и рекомендуемый способ — использовать вариант 1, где вы управляете состоянием выбранного элемента через атрибут value
на уровне select
. Это соответствует лучшим практикам работы с контролируемыми компонентами в React и обеспечивает наибольшую предсказуемость поведения вашего компонента.
Как условно добавлять атрибуты к компонентам React?
В чем разница между React Native и React?
Когда использовать JSX.Element, ReactNode и ReactElement?
Как реализовать дебаунс?
Обнаружена ошибка: Невозможное нарушение: Неверный тип элемента: ожидался строковый тип (для встроенных компонентов) или класс/функция, но получен объект