#javascript #reactjs
Вопрос:
У меня есть lt;selectgt;
тег, который я использую для создания своего собственного пользовательского lt;SelectFieldgt;
компонента следующим образом:
export default function SelectField(props) { /* PARAMS: - fieldname (String) - fieldID (String) - options (Array([Object, Object...]) [{"option_value": "option_name"}, ...]) - init_value (String "option_value") */ const generate_options = () =gt; { // Function for handling SelectField options let element_list = []; element_list.push( lt;SelectOption key={0} option_value="" option_text="None" /gt; ); var count = 1; if (!Array.isArray(props.options)) { for (var [value, name] of Object.entries(props.options)) { element_list.push( lt;SelectOption key={count} option_value={value} option_text={name} /gt; ); count = 1; } } else { props.options.forEach((subject) =gt; { element_list.push(subject.to_option()); }); } return element_list; }; const nameToString = () =gt; { // Converts props.fieldname into a properly formatted name if(props.fieldname.indexOf("_")){ var full_field_name = `${props.fieldname.split("_").join(" ")}`; return `${full_field_name[0].toUpperCase() full_field_name.slice(1)}`; }; return `${props.fieldname[0].toUpperCase() props.fieldname.slice(1)}`; }; return ( lt;div className="form-group"gt; lt;label htmlFor={props.fieldname}gt; {nameToString()}: lt;/labelgt; lt;select name={`${props.fieldname}`} id={`${props.fieldID}`} className="countries" defaultValue={props?.init_value ? `${props.init_value}` : ""} gt; {generate_options()} lt;/selectgt; lt;/divgt; ); }
(PS. Не обращайте внимания на имя класса — это действительно НЕ ДОЛЖНО иметь значения. Но кто знает, с таким новичком, как я… DS). Теперь я хочу использовать это поле для создания lt;selectgt;
тега Subjects
в своем веб-приложении (на базе Django), и я создал для него подкомпонент, который выглядит следующим образом:
export function SubjectSelectField(props) { const [subjects, setSubjects] = useState([]); useEffect(() =gt; { const getSubjects = async () =gt; { let reqObj = new RequestHandler("/courses/subjects/"); const data = await reqObj.sendRequest(); setSubjects( data.map((item) =gt; { return new Subject(item); }) ); }; getSubjects(); }, []); console.log({ subjects }); return lt;SelectField options={subjects} {...props} /gt;; }
При визуализации страницы с помощью этого компонента я получаю следующую консоль.журналы:
{ "subjects": [ { "id": 6, "name": "Art" }, { "id": 4, "name": "Biology" }, { "id": 5, "name": "Chemistry" }, { "id": 3, "name": "Geography" }, { "id": 2, "name": "Language" }, { "id": 1, "name": "Mathmatics" }, { "id": 7, "name": "Physics" }, { "id": 8, "name": "Social Studies" } ] }
For reference; this is my custom Subject
class (ES6):
export class Subject { constructor({ id, name }) { this.id = id; this.name = name; } to_option() { return ( lt;SelectOption key={this.id} option_value={this.id} option_text={this.name} /gt; ); }; }
And the lt;SelectOptiongt;
component:
export function SelectOption(props) { return lt;option value={`${props.option_value}`} gt;{`${props.option_text}`}lt;/optiongt;; }
So, the output I expect is for the lt;SelectFieldgt;
to automatically assign the selected
attribute to the option that has the value of the lt;SelectFieldgt;
‘s init_value
prop. I use the lt;SelectFieldgt;
for another type of field I call lt;CountrySelectFieldgt;
and that works just fine. The country I expect to be pre-selected is properly selected as per its init_value
.
That component looks like this:
export function CountrySelectField(props) { return ( lt;SelectField init_value={props?.student?.country ? `${props.student.country}` : ""} {...props} /gt; ); }
As I mentioned, if I pass a value to this component’s init_value
prop, it executes just as expected and renders with correct option having selected
attribute set on it.
The lt;SubjectSelectFieldgt;
doesn’t render the way I expect — with correct lt;optiongt;
having a selected
attribute set.
Why does it not work?
EDIT:
Изменил класс Subject на функциональный компонент React:
export function Subject(props){ const id = props.subject.id; const name = props.subject.name; console.log(id, name, props.subject); if(props.option){ return( lt;SelectOption key={id} option_value={id} option_text={name} /gt; ); } return( lt;h1gt;{name} - {id}lt;/h1gt; ); }
Для того, чтобы другие вещи работали, я изменил и эти вещи:
export function SubjectSelectField(props) { // ... setSubjects( data.map((item) =gt; { return lt;Subject subject={item} /gt;; }) ); // ...
Опция по-прежнему не будет выбрана автоматически.. У меня есть другие поля, которые работают в основном с той же логикой (поле выбора используется и в других местах и работает), но я явно что-то напутал здесь.
пс. Если у вас есть время для языковой полиции, у вас определенно есть время разобраться и в этом вопросе. Не так ли, @halfer? Боже.. ДС.
Комментарии:
1. По какой причине вы использовали класс для предмета? Собственные классы JavaScript не смогут работать с JSX, если вы не расширите React. Компонент. Если вы добавите это в свой класс и замените to_option на render, это может сработать. Кроме того, вы смешиваете много vars с lets и т. Д., Рекомендуется использовать ES6 let или const.
2. Вы можете обновить свой вопрос, чтобы включить в него весь соответствующий код? Что
SelectOption
такое рендеринг компонентов?3. @bopbopbop Я постараюсь сделать компонент таким образом позже сегодня и расскажу вам, как это работает. Спасибо за совет! @DrewReese Обновлен, чтобы включить определение
lt;SelectOptiongt;
компонента.
Ответ №1:
Я рад, что решил эту проблему самостоятельно, и проблема заключалась в том, что я никогда не устанавливал проверку на SubjectSelecField
отображение lt;selectgt;
поля ТОЛЬКО тогда, когда Subject
s загружаются с сервера. Например, вот так:
export function SubjectSelectField(props) { const [subjects, setSubjects] = useState([]); // Here I use 'loaded' as a check variable when rendering. const [loaded, setLoaded] = useState(false); useEffect(() =gt; { const getSubjects = async () =gt; { let reqObj = new RequestHandler("/courses/subjects/"); try{ const data = await reqObj.sendRequest(); setSubjects( data.map((item) =gt; { return lt;Subject subject={item} /gt;; }) ); // If the Subjects load properly, set 'loaded' to true. setLoaded(true); } catch(error){ console.log(`Something went wrong when trying load Subjects from server!`); console.error(error); // Else we make sure its set to false. setLoaded(false); } }; getSubjects(); }, []); console.log({ subjects }); // Here, before rendering, we check for the 'loaded' variable // to make sure the lt;selectgt; tag doesn't try to load without // the lt;optiongt; tags. Otherwise, the 'defaultValue' prop won't work. if(loaded) return lt;SelectField init_value={props.init_value} options={subjects} {...props} /gt;; return lt;spangt;Loading Subject list...lt;/spangt;; }
Спасибо вам, люди, за то, что попытались хотя бы разобраться в этом 🙂