migu migu - 3 months ago 10
React JSX Question

Set prop values in callback

I'm new to React and was wondering whether I took the right approach here.

I have a profile page called

<App />
and a lower level component called
<ContactDetails />
. I wanted to store the state of
<ContactDetails />
on
<App />
, so I only have to write AJAX logic in one place for all components. Is this thinking correct?

And more specifically, I'm interested whether the way I pass
event.target.value
to the
<App />
when the user changes the input is correct?

ContactDetails:

import React from 'react';

class ContactDetails extends React.Component {
render() {
return (
<div>
<input value={this.props.contactDetails.email} onChange={event => this.props.onContactDetailsChange(Object.assign(this.props.contactDetails, {email: event.target.value}))} />
<input value={this.props.contactDetails.firstName} onChange={event => this.props.onContactDetailsChange(Object.assign(this.props.contactDetails, {firstName: event.target.value}))} />
</div>
)
}
}

export default ContactDetails;


App:

import React from 'react';
import ReactDOM from 'react-dom';
import ContactDetails from './components/contact_details';

class App extends React.Component {
constructor(props) {
super(props);

this.state = {
contactDetails: {
email: 'a@a.com',
firstName: ''
}
}
}

render() {
return (
<ContactDetails
onContactDetailsChange={contactDetails => this.setState({ contactDetails })}
contactDetails={this.state.contactDetails}
/>
);
}
}

ReactDOM.render(<App />, document.querySelector('.container'));

Answer

This is how I would write your solution:

import React from 'react';

// Class based component handles the logic
class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      contactDetails: {
        email: 'a@a.com',
        firstName: '',
        lastName: '',
        mobile: ''
      }
    }
  }

  // One function to handle input changes
  handleContactDetailsChange = (value) => {
    // Object.assign() first argument is the target object.
    // Object.assign() returns the target object into contactDetails.
    // Second argument are the old contactDetails.
    // Third argument is the new input value. 
    // Because the third argument comes later it overwrites anything from
    // contactDetails that has the same key.
    this.setState({
      contactDetails: Object.assign({}, this.state.contactDetails, { ...value })
    });
    // { ...value } can also be written as just value, but this creates a copy.
  }

  render() {
    return (
     <ContactDetails
      onContactDetailsChange={this.handleContactDetailsChange}
      contactDetails={this.state.contactDetails}/>
    );
  }
}

// Stateless functional component.
// Takes props as an argument.
const ContactDetails = (props) => {
    // Pull of the two props we need from the props object(cleaner syntax).
    const { onContactDetailsChange, contactDetails } = props;
    return (
     <div>
       <input
        value={contactDetails.email}
        onChange={event => onContactDetailsChange({ email: event.target.value })}
       />
       <input
        value={contactDetails.firstName}
        onChange={event => onContactDetailsChange({ firstName: event.target.value })}
       />
     </div>
    )
};

export default App;

ContactDetails would normally be in it's own file. Also you modified the props by calling Object.assign(this.props.contactDetails, {firstName: event.target.value})since the target is this.props.contactDetails. The React philosophy is that props should be immutable and top-down.

Comments