I always find my self in need to get back to React's official documentation on Controlled vs Uncontrolled component. But the official docs is written with older, class-based component, in mind while React community have move on to the more recent functional component.
I keep this example here to refer to get back to in the future. So, here it is, Controlled vs Uncontrolled Functional Component example:
import "./styles.css";
import { useState, useRef } from "react";
const App = () => {
// BEGIN Controlled component example
// Note that with controlled component,
// we are using React constucts to obtain
// and maintain the value of the form input
const [value1, setValue] = useState("");
const onChange1 = (ev) => {
setValue(ev.target.value);
};
// END Controlled component example
// BEGIN Uncontrolled component example
// Note that with uncontrolle components
// we are NOT using react constucts. We
// simply using regular DOM APIs to obtain
// and maintain the state of form input
const onChange2 = (ev) => {
const value2 = window.input2.value;
window.value2.innerHTML = value2;
};
// END Unontrolled component example
// BEGIN Uncontrolled component example using ref
// If you don't want or unable to search the DOM tree
// to reach the nodes you need, you can use React 'ref'.
// Attach a ref to the component you need to refer to
// later.
//
// The component is still uncontrolled because no react state
// is associated to the component internal state. There is no
// useState used.
//
// As the official doc states, refs should be used sparingly
const refInput3 = useRef(); // this will be attached to the <input>
const refDiv3 = useRef(); // this will be attached to the output <div>
const onChange3 = (ev) => {
const value3 = refInput3.current?.value; // the value of the DOMInputElement
if (refDiv3.current) {
refDiv3.current.innerHTML = value3;
}
};
// END Uncontrolled component example using ref
return (
<div className="App">
{/* BEGIN Controlled component example */}
<div className="container">
<label className="container-label">Controlled component</label>
<input id="input1" value={value1} onChange={onChange1} />
<div>{value1}</div>
</div>
{/* END Controlled component example */}
{/* BEGIN Uncontrolled component example */}
<div className="container">
<label className="container-label">Uncontrolled component</label>
<input id="input2" onChange={onChange2} />
<div id="value2"></div>
</div>
{/* END Uncontrolled component example */}
{/* BEGIN Uncontrolled component with ref example */}
<div className="container">
<label className="container-label">Uncontrolled component with ref</label>
<input onChange={onChange3} ref={refInput3}/>
<div ref={refDiv3}></div>
</div>
{/* END Uncontrolled component with ref example */}
</div>
);
};
export default App;