0

I'm reusing a couple of external components to create my custom Combobox in strapi app. Values are received from server so I need to add options dynamically. Currently there is the following code:

import React, { useState, useEffect } from "react";
import {
  Combobox,
  ComboboxOption
} from "@strapi/design-system";

export default function ComboboxCustom({
  valuesList,
  valueSelected
}) {
  const [value, setValue] = useState('');
  const combo = (<Combobox label="Country" value={value} onChange={setValue}>
    {valuesList.map((entry) => {
      return(
        <ComboboxOption value="{entry.id}">{entry.name}</ComboboxOption>
      );
    })}
  </Combobox>);
  // setValue(valueSelected)
  return combo;
}

And everything goes good until I try so set 'selected' option basing on another set of data. In static world I could just say useState(valueSelected) and it will work. But as code generated dynamically, there is no related option yet, so I get failure like "Failed to get 'props' property of undefined".

I tried to put this combobox into a variable and set state between creation and returning it (commented setValue line before the return statement) but then app gets in a loop and returns "Too many re-renders".

Does anyone has an idea of how to change/rewrite this to be able to set selected value for dynamically created combobox?

Evgeny
  • 159
  • 1
  • 10

2 Answers2

1

So I assume that the values are dynamically fetched and passed to the ComboboxCustom. I think you can add setValue(valueSelected) inside an useEffect. onChange of the prop valueSelected.something like,

useEffect(() => {

   setValue(valueSelected)

}, [valueSelected])

Also handle the return when the value is not yet loaded. like before doing valuesList.map, first check if valueList ? (render actual) : (render empty)

Hope this helps!!

Thanks, Anu

Anu
  • 359
  • 4
  • Thanks for pointing me in this direction. Basing on your suggestion I made my code work and found also alternative solution. Pasted it as another answer as comments are unlikely give to post so long text and format code well. Please take a look and feel free to comment if something could or should be improved. – Evgeny Oct 15 '22 at 06:29
0

Finally I got working solution based on answer from @Anu. Cause valuesList is got as GET-request from another hook, I have to check values are already present (first hook hit gives [] yet) and bind Combobox state updating to change of valuesList also. Though I don't fell like this solution is perfect.

import React, { useState, useEffect } from "react";
import {
  Combobox,
  ComboboxOption
} from "@strapi/design-system";

export default function ComboboxCustom({
  valuesList,
  valueSelected,
}) {
  const [value, setValue] = useState('');
  let combo = null;
  useEffect(() => {
    if(combo && combo?.props?.children?.length > 0 && valuesList.length > 0) {
      setValue(valueSelected)
    }
 }, [valueSelected, valuesList])

  combo = (<Combobox label="Country" value={value?.toString()} onChange={setValue}>
    {valuesList.map((entry) => {
      return(
        <ComboboxOption value={entry?.id?.toString()}>{entry.name}</ComboboxOption>
      );
    })}
  </Combobox>);
  return combo;
}

After that I decided avoid creating custom component based on already published as I'll need to add and process event listeners that are added for us in the existing components. So I placed this code directly into my modal and it also works:

const [countries, setCountries] = useState([]);
const [deliveryCountryValue, setDeliveryCountryValue] = useState('');

useEffect(async () => {
  const countriesReceived = await countryRequests.getAllCountries();
  setCountries(countriesReceived);
}, []);

useEffect(() => {
  // If there is no selected value yet, set the one we get from order from server
  const valueDelivery = deliveryCountryValue != '' ? deliveryCountryValue : order.country?.id;
  if(countries.length > 0) {
    setDeliveryCountryValue(valueDelivery);
    order.country = countries.find(x => x.id == valueDelivery);
  }
}, [deliveryCountryValue, countries])


<Combobox key='delivery-combo' label="Country" value={deliveryCountryValue?.toString()} onChange={setDeliveryCountryValue}>
  {countries.map((entry) => {
   return(
      <ComboboxOption key={'delivery'+entry.id} value={entry?.id?.toString()}>{entry.name}</ComboboxOption>
    );
  })}
</Combobox>
Evgeny
  • 159
  • 1
  • 10