harpreetkhalsagtbit / country-state-city

Basic library for Country, State and City
country-state-city.vercel.app
GNU General Public License v3.0
282 stars 182 forks source link

In iphone shows error maximum call stack exceeded error while importing country-sate-city ,but working in android , window and mac #184

Open shahidforsdlc opened 4 months ago

shahidforsdlc commented 4 months ago
// LocationSelector.js
import React, { useState, useEffect } from 'react';
import { Select, Form, Input } from 'antd';
import { State, City } from 'country-state-city';

const { Option } = Select;

const LocationSelector = ({ form }) => {
  const [states, setStates] = useState([]);
  const [cities, setCities] = useState([]);
  const [selectedState, setSelectedState] = useState(null);

  useEffect(() => {
    const statesData = State.getStatesOfCountry('US');
    setStates(statesData);
  }, []);

  const handleStateChange = (value) => {
    setSelectedState(value);
    const selectedStateObj = states.find(state => state.name === value);
    const citiesData = City.getCitiesOfState('US', selectedStateObj.isoCode);
    setCities(citiesData);
    form.setFieldsValue({ city: null }); // Reset city field
  };

  const validateZipCode = (rule, value) => {
    const zipCodeRegex = /^[0-9]{5}(?:-[0-9]{4})?$/;
    // const zipCodeRegex = /^\d{5}$/;
    if (value && !zipCodeRegex.test(value)) {
      return Promise.reject('Invalid ZIP code! Please enter a 5-digit or 9 digit ZIP code. Example 12345 or 12345-1234');
    }
    return Promise.resolve();
  };

  return (
    <>
     <Form.Item
        label="Country"
        name="country"
        initialValue="USA"
        rules={[{ required: true, message: 'Please select a country!' }]}
      >
        <Select
          placeholder="Select country"
          allowClear
          optionFilterProp="children"
          showSearch
          disabled
        >
          <Option value="USA">United States</Option>
        </Select>
      </Form.Item>
      <Form.Item
        name="state"
        label="State"
        rules={[{ required: true, message: 'Please select a state!' }]}
      >
        <Select
        allowClear
        optionFilterProp="children"
        showSearch
          placeholder="Select a state"
          onChange={handleStateChange}
        >
          {states.map((state) => (
            <Option key={state.name} value={state.name}>
              {state.name}
            </Option>
          ))}
        </Select>
      </Form.Item>

      <Form.Item
        name="city"
        label="City"
        rules={[{ required: true, message: 'Please select a city!' }]}
      >
        <Select
        allowClear
        optionFilterProp="children"
        showSearch
        placeholder="Select a city" disabled={!selectedState}>
          {cities.map((city) => (
            <Option key={city.name} value={city.name}>
              {city.name}
            </Option>
          ))}
        </Select>
      </Form.Item>

      <Form.Item
        name="zipCode"
        label="ZIP Code"
        rules={[
          { required: true, message: 'Please enter a ZIP code!', validateTrigger: ['onSubmit'] },
          { validator: validateZipCode, validateTrigger: ['onSubmit'] },
        ]}
      >
        <Input placeholder="Enter ZIP code" />
      </Form.Item>
    </>
  );
};

export default LocationSelector;
Martin-Fenocchio commented 4 months ago

do you have some info about this bug? I am facing the same problem

shahidforsdlc commented 4 months ago

I've resolved the issue by creating my own package called usa-state-city, as I only need information about the USA for my project. The problem was occurring in a Vite project using the country-state-city package, and it only showed a "maximum call stack error" on iPhone. This error was due to the large JSON file used by the package, which contains information for all countries.

If you're using Vite instead of CRA and encountering similar issues, I suggest either using CRA scripts or creating your own package for specific country information. This way, you'll only include the necessary JSON data for that country, rather than the entire world's data.

Hope it helps ,What do you think ?

On Wed, 10 Jul, 2024, 7:34 pm Martín Fenocchio, @.***> wrote:

do you have some info about this bug? I am facing the same problem

— Reply to this email directly, view it on GitHub https://github.com/harpreetkhalsagtbit/country-state-city/issues/184#issuecomment-2220601717, or unsubscribe https://github.com/notifications/unsubscribe-auth/A6L2VDNLW42B66MGQAAIZLTZLU5PXAVCNFSM6AAAAABJ5UBIQSVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDEMRQGYYDCNZRG4 . You are receiving this because you authored the thread.Message ID: @.***>

terence1990 commented 2 months ago

Getting this too, so bizzare - deffo what @shahidforsdlc large non-async JSON imports on mobile cause memory issue, will try an async approach and revert back on this issue.

terence1990 commented 2 months ago

I fixed it this way with async:

const getCountries = async () => {
  const Country = await import('country-state-city/lib/country');
  const {getAllCountries} = Country.default;
  return getAllCountries().sort((a, b) => a.name.localeCompare(b.name));
};

const getCities = async (country: string, state?: string) => {
  const City = await import('country-state-city/lib/city');
  const {getCitiesOfState, getCitiesOfCountry} = City.default;
  return state ? getCitiesOfState(country, state) : getCitiesOfCountry(country) ?? [];
}

Since it's now async, becomes troublesome as need to run a side affect in react, so used a hook:

import { ICity, ICountry, IState } from 'country-state-city/lib/interface';

export const useCountriesAndCities = (props?: {
  country?: string,
  state?: string
}) => {
  const [countries, setCountries] = useState<ICountry[]>([]);
  const [cities, setCities] = useState<ICity[]>([]);

  useEffect(() => {
    getCountries().then(setCountries);
  }, []);

  useEffect(() => {
    if (props?.country && props?.state) {
      getCities(props.country, props.state).then(setCities);
    }
  }, [props?.country, props?.state]);

  const phoneCodes = useMemo(() => countries
    .map((country) => ({
      value: country.phonecode.startsWith('+')
        ? country.phonecode
        : `+${country.phonecode}`,
      label: `${country.flag} ${
        country.phonecode.startsWith('+')
          ? country.phonecode
          : `+${country.phonecode}`
      }`,
    }))
    .filter((a, i, arr) => arr.findIndex((b) => b.value === a.value) === i), [countries]);

  return {
    countries,
    cities,
    phoneCodes
  };

};

Related: https://github.com/harpreetkhalsagtbit/country-state-city/issues/173 https://github.com/harpreetkhalsagtbit/country-state-city/issues/123

modernlabrat commented 4 weeks ago

This issue still exists. I don't think this package is being managed anymore...

mzakiullahusman commented 2 weeks ago

I ran into some major problems in production with this issue. As an Android user, wasnt using any emulators to test on iOS and my web app just blew up for iOS users...took me around 2-4 hours to find the culprit