React Component Not Updating After Changing A Value

Complexity picture Complexity · Apr 19, 2017 · Viewed 9.6k times · Source

In ReactJS, I'm writing a stateless component;
Since I've read avoiding unnecessary states is best practice.

The component represents an input field which executed a function when the input box contains a value.

    export const InputField = (props) => {
      
      const InputFieldContentsChanged = (event) => {
        props.onChange(event.target.value);
      };

      return (
        <div data-component="input-field"
          className={(props.value !== "" ? "value": "")}>
          <input type={props.type} value={props.value} onChange={InputFieldContentsChanged} />
          <span className="bar"></span>
          <label>{props.label}</label>
        </div>
      );
    };

    InputField.PropTypes = {
      type: PropTypes.oneOf([ "text", "password" ]).isRequired,
      label: PropTypes.string.isRequired,
      value: PropTypes.string,
      onChange: PropTypes.func.isRequired
    }

Now, I've created another component which just is a sample to test the component above. This looks like the following:

    export const SampleComponent = (props) => {
      
      let componentUsername = "";
      
      const onUsernameChanged = (username) => {
        componentUsername = username;
      };
      
      return (
        <InputField type="text" label="Username" value={componentUsername} onChange={onUsernameChanged} />
      );
    };

So, I'm binding the value to a custom variable in the component which is changed when the contents of the input field does change.

How does it come that the input field component does not update itself with the new username?

Kind regards,

Answer

Fabian Schultz picture Fabian Schultz · Apr 19, 2017

I'm writing a stateless React component since it's best practice to avoid state when not needed.

In your code you are trying to use your own kind of "state" though, and it's just a variable (componentUsername). But since it's not React state, the component does not re-render upon the change of the variable. React simply doesn't know about the change.

So, either use the usual setState instead of re-assigning the your own "state" variable, or put the logic in the parent component and pass the componentUsername to the SampleComponent via props:

const SampleComponent = props => (
  <input type="text" onChange={props.onChange} value={props.value} />
);

class ParentComponent extends React.Component {
  constructor() {
    super();
    this.state = { value: '' };
    this.handleInputChange = this.handleInputChange.bind(this);
  }

  handleInputChange(e) {
    console.log(e.target.value);
    this.setState({ value: e.target.value });
  }

  render() {
    return (
      <SampleComponent
        value={this.state.value}
        onChange={this.handleInputChange}
      />
    );
  }
}

ReactDOM.render(<ParentComponent />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>