0

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:

  1. The form initializes with the correct market value in defaultValues
  2. But the Market select field shows the placeholder "Select market" instead of the selected market
  3. 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

  1. Logging the values: field.value shows the correct number (e.g., 5)
  2. Checking markets array: It's empty [] when form first renders, then gets populated
  3. 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.

2
  • Please edit your question to include a minimal reproducible example. Currently it's missing definitions for a bunch of the functions/components you use. Many of these are probably not relevant, so you could edit them away and still reproduce the problem. Commented Jun 3 at 13:32
  • @MattKantor I've edited the code as you asked. Commented Jun 3 at 15:57

1 Answer 1

0

You need to set the selected attribute on the <option> that you want to be selected. Try this:

<select {...form.register('market', { valueAsNumber: true })}>
  <option value="">Select market</option>
  {markets.map((market) => (
    <option
      key={market.value}
      value={market.value}
      selected={market.value === form.watch('market')}
    >
      {market.label}
    </option>
  ))}
</select>
Sign up to request clarification or add additional context in comments.

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.