Uncontrolled component Uncontrolled component data is handled by the DOM. We need to use useRef() hook to get form values from the DOM. inpRef.current refers to the DOM element. import React, { useRef, useState } from 'react'; function UncontrolledInput() { const [inpVal, setInpVal] = useState('initial text'); const inpRef = useRef('initial text') return ( <> <h1>{inpVal}</h1> <input ref={inpRef} type="text" value={inpVal} onChange={() => setInpVal(inpRef.current.value)} /> </> ); } <UncontrolledInput /> Controlled component Controlled component data is handled by a React component via useState() hook . Event handler takes care of a state update. import React, { useRef, useState } from 'react'; function ControlledInput() { const [inpVal, setInpVal] = useState('initial text'); return ( <> <h1>{inpVal}</h1> <input type="text" value={inpVal} onChange={e => setInpVal(e.target.value)} /> </> ); } <ControlledInput /> Custom hook for input Form with input With controlled component input values are always driven by the React state and we always need to create such logic for every input. We control input value via react state with onChange event On form submit we prevent the default action and do our logic After submission we reset input fields with initial empty string function FormWithInputWithoutCustomHook() { const [nameState, setNameState] = useState('') const submitHandler = e => { e.preventDefault() alert(`hello ${nameState}`) setNameState('') } return ( <form onSubmit={submitHandler}> <input placeholder="Name" value={nameState} onChange={e => setNameState(e.target.value)} /> <button>Submit</button> </form> ) } <FormWithInputWithoutCustomHook /> Custom hook for input Let's make a custom hook for an input control We extract the logic into a separate file useInput.js and export back the input value & onChange attributes in the object + reset function // functions\ useInput.js import { useState } from 'react' export default function useInput(initVal = '') { const [val, setVal] = useState(initVal) const reset = () => setVal(initVal) const bind = { value: val, onChange: e => setVal(e.target.value) } return [val, bind, reset] } Input with custom hook In the main file we bring and destruct values from the custom hook Note how we bring value and onChange attributes by spreading the object into the input field import useInput from '/functions/useInput' function FormWithInputWithCustomHook() { const [nameState, bindName, resetName] = useInput() const submitHandler = e => { e.preventDefault() alert(`hello ${nameState}`) resetName() } return ( <form onSubmit={submitHandler}> <input placeholder="Name" {...bindName} /> <button>Submit</button> </form> ) } <FormWithInputWithCustomHook />