0
votes

I'm a beginner with React and I'm attempting to make an address book application (front-end only, for now). I have my root component called App in the App.js file and I'm trying to move the form element from the root component into its own component (state-less I guess) in the AddContact.js file. I was able to find a way (searching SO) to pass the form input fields when the form is part of the root component to the form submit handler function. But I'm not able to do the same when the form element is part of a different component. Below are the App.js and AddContact.js files. How should I go about doing so? Preferably in the simplest way, while adhering to react/es6 best practices.

App.js

import React, { Component } from 'react';
import './App.css';
//import AddContact from './AddContact/AddContact';
//import './AddContact/AddContact.css';
import ContactList from './ContactList/ContactList.js';
import Cockpit from './Cockpit/Cockpit.js';


class App extends Component {
  state = {
    contacts:[]
  };

  addContactHandler = (event) => {
    event.preventDefault();
    //console.log(event.target[0].value);
    //console.log(event.target.name.value);
    const name = this.input1.value;
    const phone = this.input2.value;
    const email = this.input3.value;
    let contacts = [...this.state.contacts];
    if (name.length>0 && phone.length>0 && email.length>0){

      let contact = {
        name: name,
        phone: phone,
        email: email
      };
      contacts.push(contact);
      console.log("contacts: ", contacts);
      this.setState({
        contacts:contacts
      });
    }
    this.input1.value = '';
    this.input2.value = ''; 
    this.input3.value = '';

  };

  render() {
    let contactList = null;
    let contacts = [...this.state.contacts];
    if (contacts.length > 0) {
        contactList = (
          <ContactList
          contacts = {contacts}
          />
        );
    }
    return (
      <div className="App">
        <Cockpit/>
        <p>Add New Contact:</p>
        <form className="AddContact" onSubmit={(event)=>this.addContactHandler(event)}>
            <label>Name: 
                <input type="text" name="name" ref={(name) => {this.input1 = name}}/>
            </label>
            <label>Phone: 
                <input type="text" name="phone" ref={(phone) => {this.input2 = phone}}/>
            </label>
            <label>Email: 
                <input type="text" name="email" ref={(email) => {this.input3 = email}}/>
            </label>
            <input type="submit" value="Submit" />
        </form>
        {/* <AddContact submit={(event)=>this.addContactHandler(event)}/> */}
        {contactList}
      </div>
    );
  }
}

export default App;

AddContact.js

// Use at a later stage
import React from 'react';
import './AddContact.css';

const AddContact = (props) => {
    return (
        <div >
            <p>Add New Contact:</p>
            <form className="AddContact" onSubmit={props.submit}>
                <label>Name: 
                    <input type="text" ref={(name) => {this.input = name}}/>
                </label>
                <label>Phone: 
                    <input type="text" ref={(phone) => {this.input2 = phone}}/>
                </label>
                <label>Email: 
                    <input type="text" ref={(email) => {this.input3 = email}}/>
                </label>
                <input type="submit" value="Submit" />
            </form>
        </div>
    );
};

export default AddContact;
1

1 Answers

1
votes

Moving the form element into its own dedicated component is a great approach. But don't try to handle the child component's form submit event from the parent component. The child component owns the form, it should handle its mutations and submission too. With that in mind, this is how I'd structure the components:

  1. Your App renders the AddContact component.

  2. For your AddContact component, don't pass an onSubmit handler to it. Instead, pass an onAdd handler that expects a contact object as its argument.

  3. Make your AddContact component stateful! Is there any particular reason you would want it to be stateless? Make it stateful, let it handle form updates as part of its state (use Controlled Components). And finally, on the form onSubmit event, package up a contact object and call the props.onAdd handler with it.

This will be a cleaner, more rationally separated architecture for your components. The parent component shouldn't have to worry about a child component's form submit behavior, but should rather expect a nicely packaged up contact object.

Check this out: https://mn627xy99.codesandbox.io/

class AddContact extends React.Component {

    state = {
        name: "",
        phone: "",
        email: "",
    }

    submit = e => {
        e.preventDefault();
        this.props.onAdd(this.state);

        // Clear the form
        this.setState({name: "", phone: "", email: ""});
    }

    change = e => {
        e.preventDefault();
        this.setState({[e.currentTarget.name]: e.currentTarget.value});
    }

    render() {
        return (
            <div >
                <p>Add New Contact:</p>
                <form className="AddContact" onSubmit={this.submit}>
                    <label>Name: 
                        <input type="text" name="name" onChange={this.change} value={this.state.name} />
                    </label>
                    <label>Phone: 
                        <input type="text" name="phone" onChange={this.change} value={this.state.phone} />
                    </label>
                    <label>Email: 
                        <input type="text" name="email" onChange={this.change} value={this.state.email} />
                    </label>
                    <input type="submit" value="Submit" />
                </form>
            </div>
        );
    }
}