Skip to content

Commit

Permalink
fix(footer): useForm hook
Browse files Browse the repository at this point in the history
  • Loading branch information
Erbenos committed Dec 13, 2020
1 parent a6dec9a commit a5e7ba1
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 37 deletions.
61 changes: 24 additions & 37 deletions src/components/layout/footer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { graphql, useStaticQuery } from 'gatsby'
import React, { useContext, useEffect, useRef, useState } from 'react'
import { ThemeContext } from 'styled-components'
import { Button, ButtonSize } from '../../buttons'
import useForm, { ValidationErrors } from 'hooks/useForm'
import React, { useRef } from 'react'
import { ButtonSize } from '../../buttons'
import { Link } from '../../links'
import Section from '../section'
import * as S from './styles'

const Footer: React.FC = () => {
const theme = useContext(ThemeContext)
const [email, setEmail] = useState('')
const [isInvalid, setInvalid] = useState(false)
const [isTouched, setTouched] = useState(false)
const { values, errors, handleSubmit, handleChange } = useForm<{
email: string
}>({ email: '' }, onSuccess, validate)
const emailEl = useRef<HTMLInputElement>(null)

const { listID, accID } = useStaticQuery<{
Expand All @@ -28,28 +26,6 @@ const Footer: React.FC = () => {
}
`).site.siteMetadata.ecomail

useEffect(() => validateForm(), [email, isTouched])

const validateForm = (): void => {
const isInvalid =
!emailEl.current?.validity.valid ||
(isTouched && email.trim().length === 0)
setInvalid(isInvalid)
}

const onSubmit = (e: React.FormEvent<HTMLFormElement>): void => {
// has been validated beforehand
if (isInvalid) {
e.preventDefault()
return
}
// was never touched, force validation rerun, but this time consider empty values invalid
if (!isTouched) {
setTouched(true)
e.preventDefault()
}
}

const t = {
headings: {
ceskoDigital: 'Česko.Digital',
Expand Down Expand Up @@ -79,6 +55,19 @@ const Footer: React.FC = () => {
footnote: 'cesko.digital © 2020, Tento web používa cookies ¯\\_(ツ)_/¯',
}

function onSuccess(): void {
alert('on success')
}

function validate(): ValidationErrors<typeof values> {
const errors =
emailEl.current?.validity.valid &&
emailEl.current?.value.trim().length !== 0
? null
: { email: t.newsletter.inputErr }
return errors
}

return (
<S.Wrapper>
<S.Outer>
Expand Down Expand Up @@ -122,26 +111,24 @@ const Footer: React.FC = () => {
<S.Heading>{t.headings.newsletter}</S.Heading>
<S.NewsletterInfo>{t.newsletter.note}</S.NewsletterInfo>
<S.NewsletterForm
onSubmit={onSubmit}
onSubmit={handleSubmit}
action={`https://ceskodigital.ecomailapp.cz/public/subscribe/${listID}/${accID}`}
method="POST"
noValidate
>
<S.NewsletterFormControl>
<S.NewsletterInput
name="email"
type="email"
value={email}
value={values.email || ''}
ref={emailEl}
onChange={(e) => {
setEmail(e.target.value)
setTouched(true)
}}
onChange={handleChange}
placeholder={t.newsletter.inputPlaceholder}
/>
</S.NewsletterFormControl>
<S.NewsletterButton>{t.newsletter.subscribe}</S.NewsletterButton>
<S.NewsletterInputErrMessage
className={isInvalid ? 'is-visible' : ''}
className={errors?.email ? 'is-visible' : ''}
>
{t.newsletter.inputErr}
</S.NewsletterInputErrMessage>
Expand Down
59 changes: 59 additions & 0 deletions src/hooks/useForm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { useState, useEffect } from 'react'

export type Form<T> = {
values: T
errors: ValidationErrors<T>
handleChange: (event: React.ChangeEvent<HTMLInputElement>) => void
handleSubmit: (event: React.FormEvent<HTMLFormElement>) => void
}

export type ValidationErrors<T> = { [field in keyof T]: any } | null

const useForm = <T>(
initialState: T,
onSuccess: () => void,
syncValidator: (values: T) => ValidationErrors<T>
): Form<T> => {
const [values, setValues] = useState<T>(initialState)
const [errors, setErrors] = useState<ValidationErrors<T>>(null)
const [isSubmitting, setIsSubmitting] = useState(false)
const [isTouched, setIsTouched] = useState(false)

useEffect(() => {
if (isSubmitting && (!errors || Object.keys(errors).length === 0)) {
onSuccess()
}
setIsSubmitting(false)
}, [errors, isSubmitting])

useEffect(() => {
if (isTouched) {
setErrors(syncValidator(values))
}
}, [values])

const handleSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
if (event) event.preventDefault()
setIsTouched(true)
setErrors(syncValidator(values))
setIsSubmitting(true)
}

const handleChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
event.persist()
setIsTouched(true)
setValues((values) => ({
...values,
[event.target.name]: event.target.value,
}))
}

return {
values,
errors,
handleChange,
handleSubmit,
}
}

export default useForm

0 comments on commit a5e7ba1

Please sign in to comment.