Home > OS >  Dynamically create options from a dropdown select menu in react
Dynamically create options from a dropdown select menu in react

Time:07-07

So, I'm trying to dynamically create the options of a select dropdown, I make the fetch of an api with the states of my country, but I don't know how to access the content inside each object..

As you can see below, the data is being pulled from the API, that is, the fetch worked, but I don't know how to create the options that will be inside the Select with each object.. d

import { EmailIcon, LocationIcon } from './assets/FormSvgIcons'
import { useEffect, useState } from 'react';

const SettingsForm = () => {
 const [stateList, setStateList] = useState([]);
 const [userLocation, setUserLocation] = useState('');

 const handleLocation = () => {
    setUserLocation(e.target.value);
 }

 useEffect(() => {
    let initialStates = [];

    fetch('https://servicodados.ibge.gov.br/api/v1/localidades/estados/')
        .then(response => {
            return response.json();
        }).then(data => {
            initialStates = data.map((states) => {
                return states
            });
            console.log(initialStates);
            setStateList({states: initialStates});
        });
 }, []);

 const createDropdownOptions = () => {
    const createOptions = stateList.map((state, i) => {
        Object.keys(state).map(singleState => (
            <option value={i}>{singleState.sigla}</option>
        ))
    });
    return createOptions;
 }

 return (
 <form>
    <div className="user-country">
            <label className="white-label">
                Local
            </label>
            <div className="input-icon-wrapper">
                <div className="icon-input w-embed">
                    <LocationIcon />
                </div>
                <select 
                    className="select-field white-select w-select"
                    id="locationField"
                    name="locationField"
                    onChange={handleLocation}
                >
                    {createDropdownOptions()}
                </select>
            </div>
        </div>
 </form>
 )

I know that the error is in the createDropdownOptions function because it is responsible for creating the options, but I don't know how to do it, any light?

CodePudding user response:

I see your problem, your logic is correct, but it is poorly implemented, once you have filtered the data, it is only rendering a new component:

import { EmailIcon, LocationIcon } from "./assets/FormSvgIcons";
import React, { useEffect, useState } from "react";

export default function SettingsForm() {
  const [stateList, setStateList] = useState([]);

  useEffect(() => {
    fetch("https://servicodados.ibge.gov.br/api/v1/localidades/estados/")
      .then((response) => {
        return response.json();
      })
      .then((data) => {
        console.log(data);
        setStateList(data);
      });
  }, []);

  return (
    <form>
      <div className="user-country">
        <label className="white-label">Local</label>
        <div className="input-icon-wrapper">
          <div className="icon-input w-embed">
            <LocationIcon />
          </div>
          <select
            className="select-field white-select w-select"
            id="locationField"
            name="locationField"
            onChange={handleLocation}
          >
            {stateList.map((state) => {
              return <CreateDropdownOptions state={state} />;
            })}
          </select>
        </div>
      </div>
    </form>
  );
}

function CreateDropdownOptions({ state }) {
  return (
    <option key={state.id} value={state.sigla}>
      {state.sigla}
    </option>
  );
}

I recommend using a component for each option, this will make it easier if you later need to do some action on the

CodePudding user response:

First you could simplify your useEffect to the code below. As you are making a map where the callback returns the same object for each iteration, better you use data as it's, because the output would be the same.

useEffect(() => {
  fetch("https://servicodados.ibge.gov.br/api/v1/localidades/estados/")
    .then((response) => {
      return response.json();
    })
    .then((data) => {
      console.log(data);
      setStateList(data);
    });
}, []);

Then change createDropdownOptions to the code below. You can change the value or what's displayed to nome:

const createDropdownOptions = () => {
  const createOptions = stateList.map((state) => (
    <option key={state.id} value={state.sigla}>
      {state.sigla}
    </option>
  ));
  return createOptions;
};

And finnaly you would need to pass the event to handleLocation:

const handleLocation = (e) => {
    setUserLocation(e.target.value);
 }

CodePudding user response:

Don't overthink. Tips:

  • Keep your fetching logic as simple as possible.
  • Prefer Async Await instead of then chaining for readability.
  • Honor your state initialization. If you said it is an Array, don't set it as an object.

If you have an array, you can easily map it into jsx and generate your options. You did very well, and got really close. Take a look at the changes I've done to get it working:

import { useEffect, useState } from 'react';

export const SettingsForm = () => {
  const [stateList, setStateList] = useState([]);
  const [userLocation, setUserLocation] = useState('');

  const handleLocation = () => {
    setUserLocation(e.target.value);
  };

  useEffect(() => {
    const loadOptions = async () => {
      const data = await fetch(
        'https://servicodados.ibge.gov.br/api/v1/localidades/estados/'
      ).then((response) => {
        return response.json();
      });

      setStateList(data);
    };
    loadOptions();
  }, []);

  
  return (
    <form>
      <div className="user-country">
        <label className="white-label">Local</label>
        <div className="input-icon-wrapper">
          <div className="icon-input w-embed"></div>
          <select
            className="select-field white-select w-select"
            id="locationField"
            name="locationField"
            onChange={handleLocation}
          >
            {stateList.map((state) => {
              return (
                <option key={state.nome} value={state.nome}>
                  {state.sigla}
                </option>
              );
            })}
          </select>
        </div>
      </div>
    </form>
  );
};

Hope it helps! keep up the good work and feel free to reach out in case you're still stuck!

  • Related