Im having a bit of an issue displaying dynamic values into a select box in NextJS
I am getting values from an API endpoint, making sure that data is returned and then displaying it in a select box as below, BUT i want to add an element to the top of the ‘teams’ array and then make sure on page load that the new element is displayed first.
I have tried adding the following bit of code below and above the api call, but it keeps returning errors
const newData = {id: 1, team_name: 'Personal'}
teams.push(newData)
setSelected(newData[0])
Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
I have also tried adding a useEffect hook and then setting the selected, but this also returns errors.
Nothing I do seems to let me do this.
Any help would be amazing
const Create () => {
const [selected, setSelected] = useState()
const { data: teams, error, mutate } = useSWR('/teams', () =>
axios.get('/teams')
.then(response => response.data.data)
)
if (!teams) {
return (<LoadPageSpinner />)
}
return (
<>
<Listbox value={selected} onChange={setSelected}>
{({ open }) => (
<>
<Listbox.Label className="block text-sm font-medium leading-6 text-gray-900">Assigned to</Listbox.Label>
<div className="relative mt-2">
<Listbox.Button className="relative w-full cursor-default rounded-md bg-white py-1.5 pl-3 pr-10 text-left text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-600 sm:text-sm sm:leading-6">
<span className="block truncate">{selected}</span>
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
<ChevronUpDownIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
</span>
</Listbox.Button>
<Transition
show={open}
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
{teams.map((team) => (
<Listbox.Option
key={team.id}
className={({ active }) =>
classNames(
active ? 'bg-indigo-600 text-white' : 'text-gray-900',
'relative cursor-default select-none py-2 pl-3 pr-9'
)
}
value={team.team_name}
>
{({ selected, active }) => (
<>
<span className={classNames(selected ? 'font-semibold' : 'font-normal', 'block truncate')}>
{team.team_name}
</span>
{selected ? (
<span
className={classNames(
active ? 'text-white' : 'text-indigo-600',
'absolute inset-y-0 right-0 flex items-center pr-4'
)}
>
<CheckIcon className="h-5 w-5" aria-hidden="true" />
</span>
) : null}
</>
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</div>
</>
)}
</Listbox>
</>
)
}
>Solution :
This should do it:
const personal = { id: 1, team_name: 'Personal' }
const {
data: teams,
error,
mutate
} = useSWR('/teams', () =>
axios.get('/teams').then((response) => [personal, ...response.data.data])
)
I’m placing the extra item in a new array containing it and everything in the response.