ExamplesForm Data
import { ChangeEvent, FormEvent, useMemo, } from 'react' import { useObservable, useObservableEvent, } from 'react-rx' import {type Observable, Subject} from 'rxjs' import { map, scan, startWith, tap, withLatestFrom, } from 'rxjs/operators' import {styled} from 'styled-components' import storage from './storage' const STORAGE_KEY = '__form-submit-example__' // Create subjects for form events const formData$ = new Subject<Partial<FormData>>() const submit$ = new Subject< FormEvent<HTMLFormElement> >() interface FormData { title: string description: string } function FormDataExample() { // Handle input changes const handleChange = useObservableEvent< ChangeEvent< HTMLInputElement | HTMLTextAreaElement >, any >((change$) => change$.pipe( map((event) => ({ [event.target.name]: event.target.value, })), tap((update) => formData$.next(update)), ), ) // Handle form submissions const handleSubmit = useObservableEvent< FormEvent<HTMLFormElement>, any >((event$) => event$.pipe( tap((e) => { e.preventDefault() submit$.next(e) }), ), ) // Create form data stream const data$ = useMemo( () => formData$.pipe( startWith( storage.get(STORAGE_KEY, { title: '', description: '', }), ), scan( (data, update) => ({ ...data, ...update, }), {} as FormData, ), ), [], ) // Create submit state stream const submitState$ = useMemo( () => submit$.pipe( withLatestFrom(data$), map(([_, formData]) => formData), map((formData) => storage.set(STORAGE_KEY, formData).pipe( map(() => ({ status: 'saved' as const, result: formData, })), startWith({ status: 'saving' as const, result: null, }), ), ), startWith({ status: 'unsaved' as const, result: null, }), ), [data$], ) const formData = useObservable(data$, { title: '', description: '', }) const submitState = useObservable( // @TODO investigate why this is necessary submitState$ as unknown as Observable<{ status: 'saved' | 'saving' | 'unsaved' result: FormData | null }>, { status: 'unsaved' as const, result: null, }, ) return ( <Form onSubmit={handleSubmit}> <div> <label> <strong>Title: </strong> <input type="text" name="title" value={formData.title} onChange={handleChange} /> </label> </div> <div> <label> <strong>Description: </strong> <textarea name="description" value={formData.description} onChange={handleChange} /> </label> </div> <div> <button disabled={ submitState.status === 'saving' } > {submitState.status === 'saving' ? 'Saving…' : submitState.status === 'saved' ? 'Saved!' : 'Save'} </button> </div> </Form> ) } const Form = styled.form` label { display: block; margin-top: 10px; } input, textarea { box-sizing: border-box; width: 100%; padding: 5px; } ` export default FormDataExample