From fd743825e2485d245f04a5058523acb71d4b6689 Mon Sep 17 00:00:00 2001 From: Mikayla Dobson <93477693+innocuous-symmetry@users.noreply.github.com> Date: Sat, 11 Feb 2023 17:35:11 -0600 Subject: [PATCH] refactored form component --- client/src/components/pages/Login.tsx | 39 +++--- client/src/components/ui/Form.tsx | 167 +++++++++++++------------- 2 files changed, 111 insertions(+), 95 deletions(-) diff --git a/client/src/components/pages/Login.tsx b/client/src/components/pages/Login.tsx index 8fd6a74..b46c068 100644 --- a/client/src/components/pages/Login.tsx +++ b/client/src/components/pages/Login.tsx @@ -2,23 +2,24 @@ import { useCallback, useContext, useEffect, useState } from "react"; import { useNavigate, useParams } from "react-router-dom"; import { AuthContext, useAuthContext } from "../../context/AuthContext"; import { attemptLogin } from "../../util/apiUtils"; -import { IUserAuth } from "../../schemas"; +import { IUser, IUserAuth } from "../../schemas"; import { Button, Form, Page, Panel } from "../ui"; +import { FormConfig } from "../ui/Form"; export default function Login() { const params = new URLSearchParams(window.location.search); const redirect = params.get("redirect"); const { user, setUser } = useContext(AuthContext); + const [form, setForm] = useState(); // setup and local state const navigate = useNavigate(); - const [form, setForm] = useState(); const [input, setInput] = useState({ email: '', password: '' }); // retrieve and store state from form const getFormState = useCallback((received: IUserAuth) => { setInput(received); - }, []) + }, [input]) const handleLogin = async () => { @@ -31,25 +32,35 @@ export default function Login() { // check for logged in user and mount form useEffect(() => { if (user) navigate('/'); - setForm( - new Form({ - parent: 'login', - keys: Object.keys(input), - labels: ["Email", "Password"], - dataTypes: Object.keys(input), - initialState: input, - getState: getFormState - }).mount() - ); }, []) + // useEffect(() => { + // setForm( + + // ) + // }, [getFormState]) + + useEffect(() => { + console.log(input); + }, [getFormState]) + return (

Hello! Nice to see you again.

- { form ||

Loading...

} + +
} /> + + diff --git a/client/src/components/ui/Form.tsx b/client/src/components/ui/Form.tsx index f0541a8..7704977 100644 --- a/client/src/components/ui/Form.tsx +++ b/client/src/components/ui/Form.tsx @@ -1,13 +1,6 @@ -import { ChangeEvent, FC } from "react"; -import { v4 } from 'uuid'; -import RichText from "./RichText"; - -/** - * For the generation of more complex form objects with - * larger stateful values; expects to receive an object of - * type T to a form which can mutate T with a state setter - * of type Dispatch> -**/ +import { ChangeEvent, FC, useEffect, useState } from "react" +import { v4 } from "uuid" +import RichText from "./RichText" export interface FormConfig { parent: string @@ -20,83 +13,95 @@ export interface FormConfig { extraStyles?: string } -export default class Form { - private parent: string; - private labels: string[]; - private keys: string[]; - private dataTypes: any[] - private state: T; - private getState: (received: T) => void - private richTextInitialValue?: string; - private extraStyles?: string +interface FormProps { + parent: any + _config: FormConfig +} - constructor(config: FormConfig){ - this.parent = config.parent; - this.keys = config.keys; - this.labels = config.labels || this.keys; - this.dataTypes = config.dataTypes || new Array(this.keys.length).fill('text'); - this.state = config.initialState; - this.getState = config.getState; - this.richTextInitialValue = config.richTextInitialValue; - this.extraStyles = config.extraStyles; +const Form: FC = ({ parent, _config }) => { + type T = typeof parent; + const { getState } = _config; - this.mount(); - } + const [config, setConfig] = useState>(); + const [state, setState] = useState(); + const [contents, setContents] = useState(); - update(e: ChangeEvent, idx: number) { - let newState = { - ...this.state, - [this.keys[idx]]: e.target['value' as keyof EventTarget] - } + // initial setup + useEffect(() => { + if (!config) setConfig({ + ..._config, + labels: _config.labels ?? _config.keys, + dataTypes: _config.dataTypes ?? new Array(_config.keys?.length).fill("text"), + }); - this.state = newState; - this.getState(newState); - } + if (!state) setState(_config.initialState); + }, []) - updateRichText(txt: string, idx: number) { - this.state = { - ...this.state, - [this.keys[idx]]: txt - } + // usecallback handling + useEffect(() => { + state && getState(state); + }, [state]); - this.getState(this.state); - } + // update methods + function updateRichText(txt: string, idx: number) { + if (!config) return; - mount() { - let output = new Array(); - - for (let i = 0; i < this.keys.length; i++) { - let input: JSX.Element | null; - - if (this.dataTypes[i] == 'custom picker') { - console.log('noted!'); - this.dataTypes[i] = 'text'; + setState((prev: T) => { + return { + ...prev, + [config.keys[idx]]: txt } - - if (this.dataTypes[i] == 'TINYMCE') { - input = ( -
- - this.updateRichText(txt, i)} /> -
- ) - } else { - input = ( -
- - this.update(e, i)} - value={this.state[i as keyof T] as string}> - -
- ) - } - - output.push(input); - } - - return
{output}
; + }) } -} \ No newline at end of file + + function update(e: ChangeEvent, idx: number) { + if (!config) return; + + setState((prev: T) => { + return { + ...prev, + [config.keys[idx]]: e.target['value' as keyof EventTarget] + } + }) + } + + // mount the form once config has been loaded + useEffect(() => { + if (state && config) { + const result = config.keys.map((each: string, i: number) => { + + if (config.dataTypes![i] == 'TINYMCE') { + return ( +
+ + updateRichText(txt, i)} /> +
+ ) + } else { + return ( +
+ + update(e, i)} + value={state[i as keyof T] as string}> + +
+ ) + } + }); + + setContents(result); + + } + }, [config]); + + return ( +
+ { contents } +
+ ) +} + +export default Form; \ No newline at end of file