1

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?

3 Answers 3

1

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

Sign up to request clarification or add additional context in comments.

Comments

1

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

Comments

1

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!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.