React Hook Form Select Not Showing Pre-selected Value When Editing
I have a React form using React Hook Form and shadcn/ui Select components. When I click "Edit" on a table row to edit a broker, the form opens but the Market select field doesn't show the already assigned market value, even though it's properly set in the form's default values.
The Problem
When editing a broker:
- The form initializes with the correct market value in
defaultValues - But the Market select field shows the placeholder "Select market" instead of the selected market
- The markets array gets populated after the form renders (via API call)
import React, { useState, useEffect, useCallback } from 'react';
import { useForm } from 'react-hook-form';
// Mock data types
interface Market {
label: string;
value: number;
}
interface BrokerData {
id: number;
name: string;
region: string;
market: Market;
}
interface FormData {
name: string;
region: string;
market: number;
}
// Mock API call that simulates delay
const fetchMarkets = async (region: string): Promise<Market[]> => {
await new Promise(resolve => setTimeout(resolve, 1000)); // Simulate API delay
return [
{ label: "Stock Market", value: 1 },
{ label: "Forex Market", value: 2 },
{ label: "Crypto Market", value: 3 }
];
};
// Form Component
function EditForm({ brokerData, markets, onRegionChange }: {
brokerData: BrokerData;
markets: Market[];
onRegionChange: (region: string) => void;
}) {
const form = useForm<FormData>({
defaultValues: {
name: brokerData.name,
region: brokerData.region,
market: brokerData.market.value // This is set correctly (e.g., 2)
}
});
const regionValue = form.watch("region");
useEffect(() => {
if (regionValue) {
onRegionChange(regionValue);
}
}, [regionValue, onRegionChange]);
return (
<form>
<div>
<label>Name:</label>
<input {...form.register("name")} />
</div>
<div>
<label>Region:</label>
<select {...form.register("region")}>
<option value="US">US</option>
<option value="EU">EU</option>
</select>
</div>
<div>
<label>Market:</label>
<select {...form.register("market", { valueAsNumber: true })}>
<option value="">Select market</option>
{markets.map((market) => (
<option key={market.value} value={market.value}>
{market.label}
</option>
))}
</select>
{/* This shows correct value in console but select shows "Select market" */}
<p>Current market value: {form.watch("market")}</p>
</div>
</form>
);
}
// Parent Component
function App() {
const [editData, setEditData] = useState<BrokerData | null>(null);
const [markets, setMarkets] = useState<Market[]>([]);
const [isLoading, setIsLoading] = useState(false);
// Sample broker data
const brokerToEdit: BrokerData = {
id: 1,
name: "Test Broker",
region: "US",
market: { label: "Forex Market", value: 2 }
};
const handleEdit = useCallback(async (broker: BrokerData) => {
setEditData(broker);
// This happens AFTER form renders with empty markets array
setIsLoading(true);
try {
const fetchedMarkets = await fetchMarkets(broker.region);
setMarkets(fetchedMarkets);
} finally {
setIsLoading(false);
}
}, []);
const handleRegionChange = useCallback(async (region: string) => {
setIsLoading(true);
try {
const fetchedMarkets = await fetchMarkets(region);
setMarkets(fetchedMarkets);
} finally {
setIsLoading(false);
}
}, []);
return (
<div>
<button onClick={() => handleEdit(brokerToEdit)}>
Edit Broker
</button>
{editData && (
<div>
<h3>Edit Form:</h3>
{isLoading && <p>Loading markets...</p>}
<EditForm
brokerData={editData}
markets={markets}
onRegionChange={handleRegionChange}
/>
</div>
)}
</div>
);
}
export default App;
What I've Tried
- Logging the values:
field.valueshows the correct number (e.g.,5) - Checking markets array: It's empty
[]when form first renders, then gets populated - Manual form.setValue(): Even manually setting the value doesn't make it appear
Expected Behavior
The Market select should show "Stock Market" (or whatever the assigned market name is) when the edit dialog opens.
Question
Why isn't the React Hook Form Select showing the pre-selected value, and how can I ensure the select displays the correct market when editing a broker?
The issue seems to be related to the timing of when the markets array gets populated vs when the form renders, but I'm not sure of the best way to handle this scenario.