Move method from one component to another in React

2019-08-13 09:35发布

问题:

I am working to create a small contact app using React with ES6. I had some data displaying in the render function of a component - see the link to the question below for the original structure...

How to specify a key for React children when mapping over an array

However, because I was also putting a form on the same page and I needed to update my data in state, I had to move the data to a higher level component.

Now I'm having trouble traversing the components so that my original list of contacts shows up on the left. I had to remove most of what was in my render function on the contact-list component because it was completely breaking the build.

First, here is the address-book component with the form - this is working, both pulling in my initial 3 contacts from state, then concating new contacts from the form to the array. (Still need cleanup code here to make UI work right...)

import React from 'react';
import ContactList from './contact-list.jsx';
import ContactForm from './contact-form.jsx';
import ShortContact from './short-contact.jsx';

class AddressBook extends React.Component {

constructor() {
    "use strict";
    super();

    this.state = {
        showContacts: true,
        contacts: [
    {
        id: 1,
        fName: "aaa",
        lName: "aaaaa",
        imgUrl: "http://brainstorminonline.com/wp-content/uploads/2011/12/blah.jpg",
        email: "aaa@aaaa.com",
        phone: "999999999999"
    },
    {
        id: 2,
        fName: "bbbbb",
        lName: "bbbbbbb",
        imgUrl: "https://media.licdn.com/mpr/mpr/shrinknp_200_200/bbb.jpg",
        email: "bbb@bbb-bbb.com",
        phone: "888888888888"
    },
    {
        id: 3,
        fName: "Number",
        lName: "Three",
        imgUrl: "http://3.bp.blogspot.com/-iYgp2G1mD4o/TssPyGjJ4bI/AAAAAAAAGl0/UoweTTF1-3U/s1600/Number+3+Coloring+Pages+14.gif",
        email: "three@ccccc.com",
        phone: "333-333-3333"
    }
];
    };

}




render() {
    "use strict";
    return (
        <div className="row address-book">

            <div className="col-md-6">
                <ContactList  />
            </div>
            <div className="col-md-6">

                <button className='btn' id="contact-submit-button" type="submit" value="Create Contact">Create New Contact </button>



                <div>
                    <ContactForm  addContact={this._addContact.bind(this)}/>
                </div>
            </div>
        </div>

    );

}

_addContact (fName, lName, company, email, phone, imgURL) {
    "use strict";
    const contact = {
        id: this.state.contacts.length + 1,
        fName,
        lName,
        company,
        email,
        phone,
        imgURL
    };
    this.setState({ contacts: this.state.contacts.concat([contact]) });
}

_getContacts() {
    "use strict";
    return contactList.map((contact) => {
        "use strict";
        return (
            <ShortContact contact={contact} key={contact.id}/>)
    });
}

_getContactsTitle (contactCount) {
    "use strict";
    if(contactCount === 0) {
        return 'No Contacts';
    } else if (contactCount === 1) {
        return '1 contact';
    } else {
        return `${contactCount} contacts`;
    }
}

}

export default AddressBook;

However, the bottom 2 methods _getContacts and _getContactsTitle are the ones that are needed in my ContactForm component - which is this one:

import React from 'react';
import ShortContact from './short-contact.jsx';




class ContactList extends React.Component {



render() {

    const contacts  = this._getContacts();


    return (
        <div>
            <h3>List of Contacts</h3>
            <h4 className="contact-count">{this._getContactsTitle((contacts.length))}</h4>
            <ul className="contact-list">
                {contacts}
            </ul>
        </div>
    );
    }
}

export default ContactList;

The const that defines contacts as well as the <h4> through the <ul> is what breaks the app because as you can see it references the _getContactTitle method from the other component as well as {contacts} which is in the _getContacts method.

I'm guessing I need to do something like wrap them into functions and pass them - but I've gotten turned around and can't quite see how that would work here with React. Any help would be welcome. Thanks!

回答1:

You need to pass the contacts down via props instead of trying to build the list of contacts in the parent to pass down. what I mean is this.

ADDRESS BOOK

render() {
    "use strict";
    let contacts  = this._getContacts();
-----------^---------------^----------- get the list in the parent
    return (
        <div className="row address-book">
            <div className="col-md-6">
                <ContactList contacts={contacts} />
-------------------------------^-----------^---- important part here
            </div>
            <div className="col-md-6">
                <button className='btn' id="contact-submit-button" type="submit" value="Create Contact">Create New Contact </button>
                <div>
                    <ContactForm  addContact={this._addContact.bind(this)}/>
                </div>
            </div>
        </div>

    );
}

then in your CONTACT LIST component just use the list as props.

render() {
    let {contacts} = this.props;
------------^--------------^---- its in the props now
    let title = contacts.length > 1 ? `${contacts.length} contacts` : contacts.length === 1 ? '1 contact' : 'No Contacts';
    return (
        <div>
            <h3>List of Contacts</h3>
            <h4 className="contact-count">{title}</h4>
            <ul className="contact-list">
                {contacts}
            </ul>
        </div>
    );
    }
}

from here you can remove the contactTitle function. let the child render that since it knows the length of the array (because its in props).

As a side note, in your _addContact function. instead of creating a new array and concatenating it with the state contact array just push the new one onto the current state. its better storage (i.e. dont need to make a new array to combine a new item to the array).

this.setState({ contacts: this.state.contacts.push(contact) });