Implement a useInputControl
hook that manages a controlled input value and tracks additional form input states like:
Property | Tracks | When it becomes true | When it becomes false |
---|---|---|---|
Touched | If input has been focused then blurred | When the user blurs the input (focus -> blur) | Never resets automatically |
Dirty | If value has been changed before | When the user types something Never resets automatically | |
Different | If value is different from the original | When the value is different from the initial | When the value is same as the initial |
The handleX
functions returned by the hook are meant to be called on the relevant event handlers of <input>
in order for the hook to work as intended.
export default function Component() {const nameInput = useInputControl('Oliver');return (<form><div><label htmlFor="name">Name</label><inputid="name"value={nameInput.value}onChange={nameInput.handleChange}onBlur={nameInput.handleBlur}/></div><p>Touched: {nameInput.touched.toString()}</p><p>Dirty: {nameInput.dirty.toString()}</p><p>Different: {nameInput.different.toString()}</p><button type="submit" disabled={!nameInput.different}>Submit</button><button type="button" onClick={nameInput.reset}>Reset</button><form>);}
initialValue: string
: The initial value of the inputThe hook returns an object with the following properties:
value: string
: The current value of the inputdirty: boolean
: Whether the user has been modified at least oncetouched: boolean
: Whether the input was focused and blurreddifferent: boolean
: Whether the value is different from the initial valuehandleChange: (event: React.ChangeEvent<HTMLInputElement>) => void
: A function that updates the value of the inputhandleBlur: (event: React.FocusEvent<HTMLInputElement>) => void
: A function that to be called when the input is blurredreset: () => void
: A function to reset to the initial value as well as the value of all statesImplement a useInputControl
hook that manages a controlled input value and tracks additional form input states like:
Property | Tracks | When it becomes true | When it becomes false |
---|---|---|---|
Touched | If input has been focused then blurred | When the user blurs the input (focus -> blur) | Never resets automatically |
Dirty | If value has been changed before | When the user types something Never resets automatically | |
Different | If value is different from the original | When the value is different from the initial | When the value is same as the initial |
The handleX
functions returned by the hook are meant to be called on the relevant event handlers of <input>
in order for the hook to work as intended.
export default function Component() {const nameInput = useInputControl('Oliver');return (<form><div><label htmlFor="name">Name</label><inputid="name"value={nameInput.value}onChange={nameInput.handleChange}onBlur={nameInput.handleBlur}/></div><p>Touched: {nameInput.touched.toString()}</p><p>Dirty: {nameInput.dirty.toString()}</p><p>Different: {nameInput.different.toString()}</p><button type="submit" disabled={!nameInput.different}>Submit</button><button type="button" onClick={nameInput.reset}>Reset</button><form>);}
initialValue: string
: The initial value of the inputThe hook returns an object with the following properties:
value: string
: The current value of the inputdirty: boolean
: Whether the user has been modified at least oncetouched: boolean
: Whether the input was focused and blurreddifferent: boolean
: Whether the value is different from the initial valuehandleChange: (event: React.ChangeEvent<HTMLInputElement>) => void
: A function that updates the value of the inputhandleBlur: (event: React.FocusEvent<HTMLInputElement>) => void
: A function that to be called when the input is blurredreset: () => void
: A function to reset to the initial value as well as the value of all statesTo recap what fields are returned:
value
: The current input valuetouched
: Whether the input has been focused then blurreddirty
: Whether the value has been changed beforedifferent
: Whether the value is different from the originalLet's go through each field and how to implement them:
value
value
is a boolean value tracked using React state. An handleChange
handler us used to to update the value state from the input's change
event.
touched
touched
is a boolean value tracked using React state. It will be set to true
when the input is blurred. As the hook does not know when the <input>
element is blurred, we return a handleBlur
function that sets the value to true
and the user will call the handleBlur
function in an onBlur
event handler.
dirty
touched
is a boolean value tracked using React state. Since it is set to true
when it has been changed before, handleChange
is a good place to do that.
different
This field does not require a state as it is derived state that can be computed by comparing the initial value and the current value
. However, the comparison should not be done against the initialValue
argument as the value might be different during re-renders!
Instead, we track the first render's initialValue
using useRef
. Why not state? Because initialValueRef
does not ever change after the hook is mounted.
const different = initialValueRef.current !== value;
reset
functionThis function resets all the states within the hook. Simply call the various state setters with their initial values. The initial value to set to can be obtained from initialValueRef
.
import { ChangeEvent, useCallback, useRef, useState } from 'react';interface UseInputValueReturn {value: string;dirty: boolean;touched: boolean;different: boolean;handleChange: (event: ChangeEvent<HTMLInputElement>) => void;handleBlur: () => void;reset: () => void;}const defaultDirty = false;const defaultTouched = false;export default function useInputControl(initialValue: string,): UseInputValueReturn {const initialValueRef = useRef<string>(initialValue);const [value, setValue] = useState(initialValue);const [dirty, setDirty] = useState(defaultDirty);const [touched, setTouched] = useState(defaultTouched);const handleChange: UseInputValueReturn['handleChange'] = useCallback((event) => {setValue(event.currentTarget.value);setDirty(true);},[],);const handleBlur: UseInputValueReturn['handleBlur'] = useCallback(() => {setTouched(true);}, []);const reset = useCallback(() => {setValue(initialValueRef.current);setDirty(defaultDirty);setTouched(defaultTouched);}, []);// Derived from whether the value is different from the initial valueconst different = initialValueRef.current !== value;return {value,dirty,touched,different,handleChange,handleBlur,reset,};}
console.log()
statements will appear here.