Explain Codes LogoExplain Codes Logo

React - changing an uncontrolled input

javascript
controlled-inputs
react-components
state-management
Anton ShumikhinbyAnton Shumikhin·Nov 28, 2024
TLDR

The quick fix for changing an uncontrolled input to a controlled one is by tying the input value directly to the React state and updating it through a specified onChange event.

import React, { useState } from 'react'; function TextInput() { const [inputValue, setInputValue] = useState(''); // Lock state up with input value! return ( <input type="text" value={inputValue} // Value is in cahoots with state onChange={(e) => setInputValue(e.target.value)} // I'm the State Whisperer /> ); }

The useState spearheads state tracking and the onChange handler ensures the state and the user's input are always in perfect harmony.

The "why", "when", and "how" of controlled inputs

Controlled inputs are like your application's reliable friends who maintain the state reliably. They are primarily used to guarantee a consistent UI with the state.

Why bother?

  • They validate user inputs before embracing them.
  • Arrange the form's state in an orderly manner.
  • Ensure the input's value is a mirror of the current state anytime, anywhere.

To avoid an "uncontrolled input" fuss, always initialize your state with a defined value in either a constructor or using hooks like useState.

State transition etiquette

State initialization: Get it right

Befriend your state and define it right from the start to dodge the trouble between controlled and uncontrolled components.

class ControlledInput extends React.Component { constructor(props) { super(props); this.state = { name: 'John Doe' }; // Knocking on `this` door, let's hang out! } // ... }

Transitioning from uncontrolled to controlled

Stay clear from any null or undefined values. These guys make your input feel all free-willed and uncontrolled.

Input handlers: The Controller's attendants

Incorporate onChange handlers for all your input fields. It's like a human hand guiding your robotic arm to a user's desired state.

handleChange(event) { this.setState({value: event.target.value}); // Value, let's sync up! } render() { return ( <input type="text" value={this.state.value} onChange={this.handleChange} /> // Let's stay connected ); }

Event Binding: It's all about the "this"

Keep your event handlers in check so that they always point to the right this.

With bind:

constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); // Respect my `this` space }

Or arrow function:

handleChange = (event) => { this.setState({value: event.target.value}); // Look, `this` way! }

Checkbox and !!: A friendly union

For the checkbox friends out there, !! handles the checked state in no time:

<input type="checkbox" checked={!!this.state.isTicked} // I'm a "yes" man onChange={this.handleCheckboxChange} />

Undefined values: Like the Bermuda Triangle

Keep your input away from the pull of undefined. Even null is a no-go. An undefined value prop leads your controlled input into a maelstrom of uncontrolled.

Arrays and Objects: Keeping them orderly

Pay attention when your state has arrays or objects. Ensure not to mutate your state directly. Use spread operators or immutability helpers that keep your state untouched while making changes.

handleArrayChange = (index, newValue) => { const newArray = [...this.state.array]; newArray[index] = newValue; this.setState({ array: newArray }); };

Common traps to avoid

Steer clear of:

  • Multiple default exports starring in a module.
  • Leaving onChange handler out of the party.
  • Forgetting to tag along the value prop.