I have a form that I want to submit, and the address fields can be populated by selecting an Address that will navigate you back to the form and fill in the form's input values.
I use useNavigate()
to navigate to the Address Look-up page and pass in the form data.
const navigate = useNavigate();
navigate("address-lookup", {
state: {
postcode: form.postcode,
isIndividual: form.isIndividual,
firstName: form.firstName,
surname: form.surname,
},
});
Then, I use react-router's useLocation()
hook to get the form data:
const { state } = useLocation();
<SearchBar
value={state?.postcode ?? input}
handleOnInput={(e) => setInput(e.target.value)}
handleFind={handleFind}
/>
When I click an Address result, it navigates back to the form and passes the result's data.
const handleOnClick = (postcode, street, town, country) => {
navigate("form", {
state: {
postcode: postcode,
street: street,
town: town,
country: country,
},
});
};
Back in the Form, I set the value of the inputs by first checking if useLocation()
contains any data. If not, it gets set as its form
statue value.
<Input
value={state?.postcode ?? form.postcode}
onChange={(e) => setForm({ ...form, postcode: e.target.value })}
/>
An issue I am having is when I try to type in the Address Look-up input, it does set the value that was passed from the Form, however I can no longer change the input. Is it the way I am setting the input
state at this point?
Form:
const Form = () => {
const navigate = useNavigate();
const { state } = useLocation();
const [form, setForm] = useState({
isIndividual: state?.isIndividual ?? false,
firstName: state?.firstName ?? "",
surname: state?.surname ?? "",
postcode: state?.postcode ?? "",
street: state?.street ?? "",
town: state?.town ?? "",
country: state?.country ?? 0,
});
const countries = [
{
id: 1,
name: "UK",
},
{
id: 2,
name: "USA",
},
{
id: 3,
name: "France",
},
];
return (
<div>
<Checkbox
label="Individual"
onChange={(e, data) => {
setForm({ ...form, isIndividual: Boolean(data.checked) });
}}
/>
<div>
<Field
label="First Name"
validationState={form.firstName == "" ? "error" : "none"}
validationMessage={
form.firstName == "" ? "First Name is required." : ""
}
>
<Input
value={state?.firstName ?? form.firstName}
onChange={(e) => setForm({ ...form, firstName: e.target.value })}
/>
</Field>
</div>
<div>
<Field
label="Surname"
validationState={form.surname == "" ? "error" : "none"}
validationMessage={form.surname == "" ? "Surname is required." : ""}
>
<Input
value={state?.surname ?? form.surname}
onChange={(e) => setForm({ ...form, surname: e.target.value })}
/>
</Field>
</div>
<div className={styles.field}>
<Field
label="Postcode"
validationState={form.postcode == "" ? "error" : "none"}
validationMessage={form.postcode == "" ? "Postcode is required." : ""}
>
<Input
value={state?.postcode ?? form.postcode}
onChange={(e) => setForm({ ...form, postcode: e.target.value })}
/>
</Field>
<Button
appearance="outline"
onClick={() => {
navigate("address-lookup", {
state: {
postcode: form.postcode,
isIndividual: form.isIndividual,
firstName: form.firstName,
surname: form.surname,
},
});
}}
>
Look-up
</Button>
</div>
<div>
<Field label="Street">
<Input
value={state?.street ?? form.street}
onChange={(e) => setForm({ ...form, street: e.target.value })}
/>
</Field>
</div>
<div>
<Field
label="Town"
validationState={form.town == "" ? "error" : "none"}
validationMessage={form.town == "Town is required." ? "" : ""}
>
<Input
value={state?.field ?? form.town}
onChange={(e) => setForm({ ...form, town: e.target.value })}
/>
</Field>
</div>
<div>
<Label>Country</Label>
<Dropdown
aria-labelledby={dropdownId}
// defaultValue={state?.country ?? countries[0].id}
// defaultSelectedOptions={[state?.country ?? countries[0].id]}
onOptionSelect={(e, data) => {
setForm({ ...form, district: data.optionValue });
}}
>
{countries.map((country) => (
<Option key={country.id} value={country.id}>
{country.name}
</Option>
))}
</Dropdown>
</div>
<Button appearance="primary" onClick={handleSubmit}>
Submit
</Button>
</div>
);
};
Address Look-up (this page also contains a SearchBar
component that is reused for other Look-up pages):
const AddressLookup = () => {
const navigate = useNavigate();
const { state } = useLocation();
const [input, setInput] = useState("");
const [results, setResults] = useState([
{
postcode: "",
street: "",
town: "",
country: 0,
},
]);
const handleOnClick = (postcode, street, town, country) => {
navigate("form", {
state: {
postcode: postcode,
street: street,
town: town,
country: country,
},
});
};
const handleFind = () => {
//api call here
//setResults(...);
};
return (
<div>
<SearchBar
type="Address Look-up"
placeholderText="by Postcode"
value={state?.postcode ?? input}
handleOnInput={(e) => setInput(e.target.value)}
handleFind={handleFind}
/>
{results.map((result, index) => {
return (
<div
onClick={() =>
handleOnClick(
result.postcode,
result.street,
result.town,
result.country.id
)
}
key={index}
>
<div>{result.postcode}</div>
<div>{result.street}</div>
<div>{result.town}</div>
<div>{result.country.name}</div>
</div>
);
})}
</div>
);
};
const SearchBar = ({ value = "", handleOnInput, handleFind }) => {
return (
<div>
<Field>
{value != "" ? (
<Input value={value} onInput={handleOnInput} />
) : (
<Input onInput={handleOnInput} />
)}
</Field>
<div>
<Button appearance="primary" onClick={handleFind}>
Find
</Button>
</div>
</div>
);
};
This line:
<Input
value={state?.firstName ?? form.firstName}
onChange={(e) => setForm({ ...form, firstName: e.target.value })}
/>
notice your value
is using the state
object. I don't think you want that. But you're already setting the form
object to match the state
object when you call useState
.
So I think just change to this:
<Input
value={form.firstName}
onChange={(e) => setForm({ ...form, firstName: e.target.value })}
/>