Skip to main content

Computed Form Fields Example

This example shows you how to build a form with computed form fields. Computed form fields automatically calculate a value based on a separate read-only field. For example, if a user inputs a quantity, a computed form field can multiply that number by a price to give a total cost.

Learn more about RADFish examples at the official documentation. Refer to the RADFish GitHub repo for more information and code samples.

Preview

This example will render as shown in this screenshot:

Computed Form Fields

Steps

1. Define Constants for Input Fields

Before building out your form, define constants for each input field. Using constants helps reduce errors and makes your logic more maintainable.

const SPECIES = "species";
const NUMBER_OF_FISH = "numberOfFish";
const COMPUTED_PRICE = "computedPrice";

In this example, we will build a form with three inputs. The values from the first two inputs will compute the value of computedPrice.

2. Initialize the Form Component with State

Next, we'll define the main form component. To manage the data entered in the form, we initialize it with a formData state variable, which is an empty object. This state will dynamically store the values of the form fields as users interact with the form.

The setFormData function allows us to update the state whenever an input changes. This ensures the form data is kept in sync.

const ComputedForm = () => {
const [formData, setFormData] = useState({});

return (
// form JSX will go here
);
};

3. Structuring the Form with Inputs

In the return statement of the component, use the Trussworks Form component to structure your form. Within the form, include input components such as TextInput, referencing the variables defined in Step 1 (e.g. NUMBER_OF_FISH) instead of hardcoding strings. This practice helps avoid typos and ensures consistency when accessing the formData state. For better organization and styling, wrap your inputs within the FormGroup component provided by Trussworks.

return (
<Form
onSubmit={handleSubmit}
className="maxw-full margin-205 padding-205 bg-white radius-8px shadow-2"
>
<FormGroup>
<Label className="text-bold" htmlFor={NUMBER_OF_FISH}>
Number of Fish
</Label>
<TextInput
className="text-bold"
id={NUMBER_OF_FISH}
name={NUMBER_OF_FISH}
type="number"
placeholder="0"
value={formData[NUMBER_OF_FISH] || ""}
/>

4. Adding Input Handlers for Form Fields

Use handleNumberFishChange and handleSelectChange to manage the state for the Number of Fish and Species fields. These functions update the formData state whenever the user interacts with the inputs.

For the Number of Fish input, the handleNumberFishChange function captures the input value and updates the corresponding field in the formData state. Similarly, the handleSelectChange function updates the state when the user selects a species.

This function handles changes in the Number of Fish input field:

const handleNumberFishChange = (event, formData) => {
const { value } = event.target;
setFormData({
...formData, // Preserve existing form data
[NUMBER_OF_FISH]: value, // Update the "Number of Fish" field
});
};

Use the handleNumberFishChange function to manage the Number of Fish input:

<TextInput
className="text-bold"
id={NUMBER_OF_FISH}
name={NUMBER_OF_FISH}
type="number"
placeholder="0"
value={formData[NUMBER_OF_FISH] || ""} // Display the current state value
onChange={(event) => handleNumberFishChange(event, formData)} // Trigger the handler on change
/>

5. Calculating Computed Price

In addition to updating the input value of numberOfFish, we also want to update the value of computedPrice. We can do that by adding a helper function to run some logic to compute that value. We then call that helper function in the input's onChange handler:

This maps species to their respective prices:

const speciesPriceMap = {
grouper: 25.0,
salmon: 58.0,
marlin: 100.0,
mahimahi: 44.0,
};

This function is kept outside the component since it doesn't rely on React state or lifecycle:

const computeFieldValue = (numberOfFish, species) => {
let computedPrice = parseInt(numberOfFish || 0) * parseInt(speciesPriceMap[species] || 0);
return computedPrice.toString();
};

This function updates the Number of Fish and dynamically computes the "Computed Price":

const handleNumberFishChange = (event, formData) => {
const { value } = event.target;
setFormData({
...formData,
[NUMBER_OF_FISH]: value,
[COMPUTED_PRICE]: computeFieldValue(value, formData?.species || ""), // Compute and update the "Computed Price"
});
};