Last active
October 7, 2022 19:08
-
-
Save jolyndenning/a43bf0fb7f4de99769625bb950bbd04b to your computer and use it in GitHub Desktop.
Exploring some ideas
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { ChangeEvent, FormEvent } from "react" | |
import { Trigger, useActions } from './Actions' | |
function Form(props: FormProps) { | |
const [state, act, modify, respond] = useActions<FormState>(props) | |
respond(Trigger.Mount, fetchCountries) | |
respond(Trigger.Change, updateProvinceList, state.birthCountry?.countryCode) | |
return ( | |
<form onSubmit={act(submitActor)}> | |
<label> | |
First Name: | |
<input maxLength={props.maxInputLength} value={state.firstName} onChange={modify(firstNameUpdater)} /> | |
</label> | |
<label> | |
Birth Country: | |
<select value={state.birthCountry?.countryCode ?? ""}> | |
{state.countries.map(country => ( | |
<option key={country.countryCode} value={country.countryCode}> | |
{country.name} | |
</option> | |
))} | |
</select> | |
</label> | |
<label> | |
Province: | |
<select value={state.birthCountry?.countryCode ?? ""}> | |
{state.provinces.map(province => ( | |
<option key={province.abbreviation} value={province.abbreviation}> | |
{province.name} | |
</option> | |
))} | |
</select> | |
</label> | |
<button type="submit"> | |
Submit | |
</button> | |
</form> | |
) | |
} | |
function firstNameUpdater(evt: ChangeEvent<HTMLInputElement>) { | |
// Simple modifiers return state updates | |
return { | |
firstName: evt.target.value | |
} | |
} | |
async function updateProvinceList(state: FormState, props: FormProps, abortController: AbortController, setState: (state: Partial<FormState>) => any) { | |
if (!state.birthCountry) { | |
return | |
} | |
setState({ | |
provinces: [] | |
}) | |
const response = await fetch(`/api/provinces/${state.birthCountry}`, { | |
signal: abortController.signal | |
}) | |
if (!response.ok) { | |
throw Error(`Failed to fetch provinces, server responded with status ${response.status} ${response.statusText}`) | |
} | |
return { | |
provinces: await response.json() | |
} | |
} | |
async function fetchCountries(state: FormState, props: FormProps, abortController: AbortController) { | |
const response = await fetch(`/api/countries`, { | |
signal: abortController.signal | |
}) | |
if (!response.ok) { | |
throw Error(`Failed to fetch countries, server responded with status ${response.status} ${response.statusText}`) | |
} | |
return { | |
countries: await response.json() | |
} | |
} | |
function submitActor(evt: FormEvent<HTMLFormElement>) { | |
// Occurs synchronously during event firing | |
evt.preventDefault(); | |
// Complex actors return "action" functions | |
return async (state: FormState, props: FormProps, abortController: AbortController) => { | |
// Occurs inside of an effect | |
const response = await fetch(`/api/contacts`, { | |
method: "POST", | |
// AbortController cancels if component is unmounted | |
signal: abortController.signal, | |
body: JSON.stringify({ | |
firstName: state.firstName | |
}), | |
headers: { | |
'content-type': 'application/json' | |
} | |
}) | |
if (!response.ok) { | |
throw Error(`Create contacts API responded with HTTP status ${response.status} ${response.statusText}`) | |
} | |
} | |
} | |
interface FormProps { | |
maxInputLength: number; | |
} | |
interface FormState { | |
firstName: string; | |
birthCountry?: Country; | |
countries: Country[]; | |
provinces: Province[] | |
} | |
interface Country { | |
name: string; | |
countryCode: string; | |
} | |
interface Province { | |
name: string; | |
abbreviation: string; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment