React custom country select hook

Viewed 78

I am trying to create a reusable country select functionality by utilizing React Hooks. The idea behind the scenes is to create a function which would handle the country change within a custom select box. With this in mind, I have attempted the following code, which results in an endless loop. Is there any alternative to this code, or any ideas on how to implement it?

const { useState } = require("react");

const useCountrySelect = (defaultCountires, defaultCountry) => {
  const [countries, setCountries] = useState([]);

  const filteredCountries = defaultCountires.filter(
    (country) => country !== defaultCountry
  );

  setCountries(filteredCountries);

  return [countries, setCountries];
};

export default useCountrySelect;

Hook usage in a component:

const [country] = useCountrySelect(["lv", "ee", "lt"], "lt");
console.log(country);
4 Answers

Now, Why is your code resulting in an endless loop? it is because when u call useCountrySelect hook it will call setCountries(filteredCountries). now state is changed. react tries re-render the component. but in the rerendering process, setCountries(filteredCountries) will be called again. this will continue like a loop.

try this,

import {useState} from 'react'

const useCountrySelect = (defaultCountires, defaultCountry) => {
  const [countries, setCountries] = useState(() => {
    return defaultCountires.filter(
    (country) => country !== defaultCountry
  );
  });

  return [countries, setCountries];
};

export default useCountrySelect;

setCountries(filteredCountries); is unconditionally enqueueing state updates and triggering the render looping. Just set the state initially.

const { useState } = require("react");

const useCountrySelect = (defaultCountires = [], defaultCountry) => {
  const [countries, setCountries] = useState(defaultCountires.filter(
    (country) => country !== defaultCountry
  ));

  return [countries, setCountries];
};

export default useCountrySelect;

Edit react-custom-country-select-hook

Usage:

const [country] = useCountrySelect(["lv", "ee", "lt"], "lt");
console.log(country);

Output:

["lv", "ee"]

Don't understand why your state variable is an array. It seems you want the custom hook to return the selected country as well as a function to update it.

If my understanding is correct, here is a possible solution :

const { useState } = require("react");

function useCountrySelect(countries, defaultCountry) {
  const [country, setCountry] = useState(defaultCountry);

  // reset the country when input countries change
  useEffect(() => setCountry(defaultCountry), [countries.join()]);

  function setCountryWrapper(nextCountry) {
    // prevent updates with invalid country
    if (!countries.includes(nextCountry) {
      return;
    }

    setCountry(country);
  }

  return [country, setCountryWrapper, countries];
};

export default useCountrySelect;

Usage

const [country, setCountry] = useCountrySelect(["lv", "ee", "lt"], "lt");
console.log(country);
const { useState, useEffect } = require("react");

function filter(defaultCountires, defaultCountry) {
  const filteredCountries = defaultCountires.filter(
    (country) => country !== defaultCountry
  );
  return filteredCountries;
}

const useCountrySelect = (defaultCountires, defaultCountry) => {
  const [countries, setCountries] = useState(
    filter(defaultCountires, defaultCountry) // this will set the initial state
  );

  // side-effects like this should go inside `useEffect`
  useEffect(() => {
    setCountries(filter(defaultCountires, defaultCountry));
  }, [defaultCountires, defaultCountry]); // based on prop change, this effect will rerun again

  // don't set any state directly in the root component/hook body! Else you'll be stuck in an endless loop

  return [countries, setCountries];
};

export default useCountrySelect;
Related