Элемент выбора JSX, похоже, не выполняет автоматический выбор (реализует «выбранный») при выборе опции. Я что-то упускаю?

#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;; }  

Спасибо вам, люди, за то, что попытались хотя бы разобраться в этом 🙂