Dependent selects: country, state, city
Three pickers where each one's options depend on the one before it: pick a country to get its states, then a state to get its cities. Each field waits, disabled, until the one above it has a value, and changing an earlier choice clears the ones after it.
Loading…
<View style={{ gap: 14 }}> <CountryPicker label="Country" countries={COUNTRIES} value={country} onChange={(code) => { setCountry(code); setState(null); setCity(null); }} /> <SelectPicker label="State or province" placeholder={country ? 'Select…' : 'Choose a country first'} disabled={!country} options={toOptions(states)} value={state} onChange={(next) => { setState(next); setCity(null); }} /> <SelectPicker label="City" placeholder={state ? 'Select…' : 'Choose a state first'} disabled={!state} options={toOptions(cities)} value={city} onChange={setCity} /> </View>
How it works​
- The options come from the value above.
statesandcitiesare worked out on each render fromcountryandstate, so there's nothing to keep in sync. - A new choice clears what's below it. Changing the country resets the state and city, and changing the state resets the city, so a city never belongs to another country.
- A field waits for the one above.
disabledgreys it out until there's something to pick, and the placeholder says what to choose first. countrieslimitsCountryPickerto the countries you ship to, with their flags and names in any language.
With a server​
When the lists come from an API, load each one when its parent changes. With loadOptions, the parent's value goes into the request, and a key starts the list over when it changes:
<SelectPicker
key={country ?? 'none'}
label="City"
disabled={!country}
loadOptions={(query) => api.cities({ country, query })}
value={city}
onChange={setCity}
/>
For a short list that doesn't need search, fetch it in an effect when the parent changes and pass it as options, with loading while it's on its way.
In a form​
With React Hook Form, watch the parent and clear the children with setValue:
const country = watch('country');
<Controller
name="country"
control={control}
render={({ field }) => (
<CountryPicker
label="Country"
value={field.value}
onChange={(code) => {
field.onChange(code);
setValue('state', null);
setValue('city', null);
}}
/>
)}
/>