Last active
February 27, 2020 13:00
-
-
Save NickFoden/42fd969500459fe511efd116175b8620 to your computer and use it in GitHub Desktop.
React-Hook-Form
This file contains hidden or 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
// https://react-hook-form.com/get-started | |
import React from 'react' | |
import { useForm } from 'react-hook-form' | |
// The following component is an example of your existing Input Component | |
const Input = ({ label, register, required }) => ( | |
<> | |
<label>{label}</label> | |
<input name={label} ref={register({ required })} /> | |
</> | |
) | |
// you can use React.forwardRef to pass the ref too | |
const Select = React.forwardRef(({ label, register }, ref) => ( | |
<> | |
<label>{label}</label> | |
<select name={label} ref={ref}> | |
<option value="20">20</option> | |
<option value="30">30</option> | |
</select> | |
</> | |
)) | |
export default function App() { | |
const { register, handleSubmit } = useForm() | |
const onSubmit = data => console.log(data) | |
return ( | |
<form onSubmit={handleSubmit(onSubmit)}> | |
<Input label="First Name" ref={register} required /> | |
<Select label="Age" ref={register} /> | |
<input type="submit" /> | |
</form> | |
) | |
} |
This file contains hidden or 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, { useState } from 'react'; | |
import PropTypes from 'prop-types'; | |
import { ErrorDiv, Input, Label, wrapperCss } from './InputBox.styles'; | |
const InputBox = props => { | |
const { errorMsg, errorObj, id, label, name, maxLength, onChange, placeHolderText, testId, type, validate, ...rest } = props; | |
// passing in an errorObj of all the react-hook-form error | |
// Farther down I am doing a check for a current errror with `errorObj[name]` | |
const [state, setState] = useState({ | |
error: false, | |
isFocused: false, | |
smallLabel: false, | |
touched: false, | |
}); | |
const { error, isFocused, smallLabel } = state; | |
const onInputFocus = () => { | |
setState({ | |
...state, | |
smallLabel: true, | |
isFocused: true, | |
}); | |
}; | |
const validation = () => { | |
// if react-hook-form has an error for this form field | |
if (errorObj[name]) { | |
setState({ | |
...state, | |
error: errorMsg, | |
}); | |
} else { | |
setState({ | |
...state, | |
error: false, | |
}); | |
} | |
}; | |
const onInputBlur = e => { | |
// if react-hook-form has an error for this form field | |
if (errorObj[name]) { | |
setState({ | |
...state, | |
error: errorMsg, | |
isFocused: false, | |
}); | |
} else if (e.target.value === '') { | |
setState({ | |
...state, | |
isFocused: false, | |
smallLabel: false, | |
}); | |
} else { | |
setState({ | |
...state, | |
error: false, | |
isFocused: false, | |
}); | |
} | |
}; | |
const handleChange = e => { | |
onChange(e.target.value); | |
if (error) { | |
validation(e); | |
} | |
}; | |
const renderLabel = () => { | |
const className = `${placeHolderText || smallLabel ? 'small ' : ''} ${isFocused ? 'focus ' : ''} ${error ? ' error' : ''}`; | |
if (label) { | |
return ( | |
<Label htmlFor={id} className={className}> | |
{label} | |
</Label> | |
); | |
} | |
return null; | |
}; | |
const renderError = () => { | |
if (errorObj[name]) { | |
return <ErrorDiv>{error}</ErrorDiv>; | |
} | |
return null; | |
}; | |
return ( | |
<div className={wrapperCss}> | |
{renderLabel()} | |
// This version uses the react-hook-form Controller in stories file for the ref so doesn't use ref as a prop here | |
<Input | |
autoCapitalize="off" | |
autoComplete="off" | |
autoCorrect="off" | |
data-auid={testId} | |
data-error={!!error} | |
id={id} | |
maxLength={maxLength} | |
onBlur={e => onInputBlur(e)} | |
onChange={e => handleChange(e)} | |
onFocus={() => onInputFocus()} | |
placeholder={placeHolderText} | |
type={type} | |
// eslint-disable-next-line react/jsx-props-no-spreading | |
{...rest} | |
name={name} | |
/> | |
{renderError()} | |
</div> | |
); | |
}; | |
InputBox.propTypes = { | |
id: PropTypes.string, | |
label: PropTypes.string, | |
maxLength: PropTypes.number, | |
name: PropTypes.string, | |
onChange: PropTypes.func.isRequired, | |
placeHolderText: PropTypes.string, | |
type: PropTypes.string, | |
validate: PropTypes.func, | |
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), | |
}; | |
InputBox.defaultProps = { | |
id: '', | |
label: null, | |
maxLength: 51, | |
name: '', | |
placeHolderText: '', | |
type: 'text', | |
validate: null, | |
value: '', | |
}; | |
export default InputBox; |
This file contains hidden or 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, { useState } from 'react'; | |
import { useForm, Controller } from 'react-hook-form'; | |
import { storiesOf } from '@storybook/react'; | |
import { withThemesProvider } from 'storybook-addon-emotion-theme'; | |
import styled from 'react-emotion'; | |
import { useScreenSize } from '../../utils/mediaEffects'; | |
import checkoutTheme from '../theme'; | |
import InputBox from './InputBox'; | |
// import { nameFieldValidate, phoneValidate } from '../validations'; | |
import { formatPhoneNumber } from '../../utils/stringUtils'; | |
const FIRST_NAME_ERROR = 'Please enter a valid first name'; | |
const LAST_NAME_ERROR = 'Please enter a valid last name'; | |
const PHONE_ERROR = 'Please enter a valid phone number'; | |
const StyledDiv = styled('div')` | |
margin: 10px 20px; | |
width: 250px; | |
max-width: ${props => props.maxWidth}; | |
`; | |
storiesOf('Checkout/InputBox', module) | |
.addDecorator(withThemesProvider([checkoutTheme])) | |
.add('Default', () => { | |
const { | |
control, | |
register, | |
// handleSubmit, | |
watch, | |
errors, | |
} = useForm(); | |
// const onSubmit = data => { | |
// // eslint-disable-next-line no-console | |
// console.log(data); | |
// }; | |
// Watch input values via their "name" | |
// eslint-disable-next-line no-console | |
console.log(watch('firstName')); | |
// eslint-disable-next-line no-console | |
console.dir(errors); | |
const windowSize = useScreenSize(); | |
const [placeHolder, setPlaceHolder] = useState(''); | |
const [fName, setFName] = useState(''); | |
const [lName, setLName] = useState(''); | |
const [mobNumber, setMobNumber] = useState(''); | |
return ( | |
<div> | |
<h5 style={{ margin: '10px' }}>InputBox component with different labels and validations</h5> | |
<StyledDiv maxWidth={windowSize.innerWidth - 25}> | |
// One way to setup is with a Controller HOC | |
// https://react-hook-form.com/advanced-usage#ControlledmixedwithUncontrolledComponents | |
// Validation not working for me yet this way | |
<Controller | |
as={<InputBox onChange={setPlaceHolder} value={placeHolder} />} | |
name="firstName" | |
errorObj={errors} | |
control={control} | |
defaultValue="" | |
ref={register({ pattern: /^[A-Za-z]+$/i })} | |
/> | |
// I also tried the other method of passing a ref and using reactForwarRef, but couldn't get the "register" function working. | |
// https://react-hook-form.com/get-started#Adaptingexistingform | |
<InputBox | |
errorObj={errors} | |
id="dummy" | |
label="Using Place Holder Text" | |
name="placeHolder" | |
placeHolderText="hello no validations on me" | |
onChange={setPlaceHolder} | |
ref={register({ pattern: /^[A-Za-z]+$/i })} | |
value={placeHolder} | |
/> | |
</StyledDiv> | |
<StyledDiv maxWidth={windowSize.innerWidth - 25}> | |
<InputBox | |
errorObj={errors} | |
errorMsg={FIRST_NAME_ERROR} | |
id="firstName" | |
label="First Name" | |
name="firstName" | |
onChange={setFName} | |
value={fName} | |
ref={register({ pattern: /^[A-Za-z]+$/i })} | |
// validate={e => nameFieldValidate(e, FIRST_NAME_ERROR)} | |
/> | |
</StyledDiv> | |
<StyledDiv maxWidth={windowSize.innerWidth - 25}> | |
<InputBox | |
errorObj={errors} | |
errorMsg={LAST_NAME_ERROR} | |
id="lastName" | |
label="Last Name" | |
name="lastName" | |
onChange={setLName} | |
ref={register({ pattern: /^[A-Za-z]+$/i })} | |
value={lName} | |
/> | |
</StyledDiv> | |
<StyledDiv maxWidth={windowSize.innerWidth - 25}> | |
<InputBox | |
errorObj={errors} | |
errorMsg={PHONE_ERROR} | |
id="mobileNumber" | |
type="tel" | |
label="Mobile Number" | |
name="mobileNumber" | |
onChange={setMobNumber} | |
ref={register({ pattern: /^[A-Za-z]+$/i })} | |
value={formatPhoneNumber(mobNumber)} | |
/> | |
</StyledDiv> | |
</div> | |
); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment