Boutran
Boutran

Reputation: 10144

Is it possible to define a React uncontrolled functional component (without class)?

A checkbox such as the one below triggers the dreaded "Input elements must be either controlled or uncontrolled" warning

const Checkbox = props =>
  <label className="checkbox">
    <input type="checkbox" checked={props.checked} defaultChecked={false}/>
 </label>

All examples of uncontrolled components use the classed based approach for defining components, and I have found none that used functional component.

Is it possible at all to define an uncontrolled component defined with a function ?

Upvotes: 0

Views: 816

Answers (3)

Salmin Skenderovic
Salmin Skenderovic

Reputation: 1720

Here is my second approach: https://codesandbox.io/s/fervent-glitter-7nxy5?file=/src/App.js:0-1090

I don't think this is how I would design a component, but it is basically what you are asking for. A component that doesn't use the onChange-binding but instead uses useRef and updates the checkbox that way.

export default function App() {
  const [checked, setChecked] = useState(false);
  const [checked2, setChecked2] = useState(true);

  return (
    <div className="App">
      <h2>Usecase #1</h2>
      <div>currently: {checked ? "true" : "false"}</div>
      <button onClick={e => setChecked(!checked)}>toggle</button>
      <Checkbox checked={checked} setChecked={setChecked} />

      <hr />
      <h2>Usecase #2</h2>
      <div>currently: {checked2 ? "true" : "false"}</div>
      <button onClick={e => setChecked2(!checked2)}>toggle</button>
      <Checkbox checked={checked2} setChecked={setChecked2} />

      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

const Checkbox = ({ checked, setChecked }) => {
  const checkboxRef = useRef(null);

  useEffect(() => {
    // Update from props
    checkboxRef.current.checked = checked;

    const handleChange = event => setChecked(event.target.checked);
    // Update parent from change event
    checkboxRef.current.addEventListener("change", handleChange);

    // Cleanup
    return checkboxRef.current.removeEventListener("change", handleChange);
  }, [checked, setChecked]);

  return (
    <label className="checkbox">
      <input type="checkbox" ref={checkboxRef} />
    </label>
  );
};

Upvotes: 1

HermitCrab
HermitCrab

Reputation: 3274

Remove the checked and it will become uncontrolled:

const Checkbox = props =>
  <label className="checkbox">
    <input type="checkbox" defaultChecked={false}/>
 </label>

When you add checked, React also expects onChange. It's either both for controlled, or none for uncontrolled

Upvotes: 0

Salmin Skenderovic
Salmin Skenderovic

Reputation: 1720

You can just have an empty onChange:

const Checkbox = props => (
  <label className="checkbox">
    <input type="checkbox" checked={props.checked} onChange={() => void 0} />
  </label>
);

Upvotes: 0

Related Questions