Ahmed Eid
Ahmed Eid

Reputation: 4804

React stateless component with controlled form Elements?

I have a dumb/stateless component which is used to only render a form ,

just a typical form .

import React from 'react'

export const AuthorForm = 
({ firstName , lastName , handlefnChange , handlelnChange}) => (
  <form action="">
    <h2>Manage Authors</h2>
    <label htmlFor="firstName">First Name</label>
    <input type="text" name="firstName" 
      value={firstName} onChange={handlefnChange} />
    <br/>
    <label htmlFor="lastName">Last Name</label>
    <input type="text" name="lastName" 
      value={lastName} onChange={handlelnChange} />
    <br/>
    <input type="submit" value="save" />
  </form>
)

I'm controlling this form from the parent smart component

which is just rendering the upper form component passing down props for values and event handlers

import React , {Component} from 'react'
import {AuthorForm} from './'

export class ManageAuthors extends Component {
  constructor(){
    super()
    this.state = {
      author:{
        id: "",
        firstName:"", 
        lastName:""
      }
    }
  }

  handlefnChange = e => {
    this.setState({
      author:{
        firstName: e.target.value
      }
    })
  }

  handlelnChange = e => {
    this.setState({
      author: {
        lastName: e.target.value
      }
    })
  }

  render = () => (
    <div>
    <AuthorForm 
      {...this.state.author}
      handlefnChange={this.handlefnChange} 
      handlelnChange={this.handlelnChange} />
    </div>
  )
}

everything works fine but I'm getting this warning

warning.js:36 Warning: AuthorForm is changing a controlled input of type text to be uncontrolled. Input elements should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://facebook.github.io/react/docs/forms.html#controlled-components

can I resolve this warning without converting to stateful component ?

Upvotes: 3

Views: 3611

Answers (1)

Yangshun Tay
Yangshun Tay

Reputation: 53119

This is because the author object loses either the firstName or lastName field when you modify either one of them:

handlefnChange = e => {
    this.setState({
      author: {
        firstName: e.target.value
        // lastName is missing!
      }
    });
  }

handlelnChange = e => {
  this.setState({
    author: {
      // firstName is missing!
      lastName: e.target.value
    }
  })
}

React only does a merge on the top layer of this.state. Since firstName and lastName are nested inside an author object, when you do handlefn/lnChange and only set one of the fields, the other goes missing.

The fix would be to do:

handlefnChange = e => {
    this.setState({
      author: {
        firstName: e.target.value,
        lastName: this.state.author.lastName
      }
    });
  }

handlelnChange = e => {
  this.setState({
    author: {
      firstName: this.state.author.firstName,
      lastName: e.target.value
    }
  })
}

Or if you have more than two fields in future, it would be easier to use the spread operator for merging:

handlefnChange = e => {
  this.setState({
    author: {
      ...this.state.author,
      firstName: e.target.value,
    }
  });
}

handlelnChange = e => {
  this.setState({
    author: {
      ...this.state.author,
      lastName: e.target.value
    }
  })
}

Or use a utility merge function from lodash.

Upvotes: 7

Related Questions