官术网_书友最值得收藏!

  • React Components
  • Christopher Pitt
  • 887字
  • 2021-07-09 19:34:45

Nesting components

Let's think about how we want to structure the components of our interface. Many content management systems feature lists of items—items that we store in and retrieve from a database. For example, let's imagine a system through which we can manage the pages of a website.

For such a system, we need an entry-point—something like PageAdmin, which connects our persistence layer to our interface:

import React from "react";

class PageAdmin extends React.Component {
    render() {
        return <ol>...page objects</ol>;
    }
}

export default PageAdmin;

We can also represent the persistence layer in the form of a backend class:

class Backend {
    getAll() {
        // ...returns an array of pages
    }

    update(id, property, value) {
        // ...updates a page
    }

    delete(id) {
        // ...deletes a page
    }
}
Note

Later, we'll look at ways of persisting this data. For now, it's OK to just use static data in this class.

We could connect PageAdmin to this class by proving an instance of Backend as a property:

var backend = new Backend();

ReactDOM.render(
    <PageAdmin backend={backend} />,
    document.querySelector(".react")
);

Now, we can start using the Backend data in our PageAdmin component:

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

        this.state = {
            "pages": []
        };
    }

    componentWillMount() {
        this.setState({
 "pages": this.props.backend.getAll()
 });
    }

    render() {
        return <ol>
            {this.state.pages.map(function(page) {
 return <li key={page.id}>a new page</li>
 })}
        </ol>;
    }
}
Note

The truth is that we don't really need to define a default state, or store the page objects to the state. I've done so to demonstrate the idiomatic way of defining initial component state and overriding state when working with ES6-style components.

There's a lot going on here, so let's break it down bit-by-bit:

  • We made a constructor. In the constructor, we defined the initial state of a component. We defined the state as an object with an empty pages array.
  • React will call a few magic methods in the life cycle of a component. We used componentWillMount to get an array of pages, so we have something to render. We also passed this array of pages to the setState method. This exists to store state data and update the markup of a component at the same time. The this.state.pages method will now contain the array of pages from the backend.
  • When we use curly braces inside markup, it acts like a dynamic value (just like with properties). We can use the Array.prototype.map method to return a new element for each page in the array of pages. This will return a new list of li components. React also expects components in a list to have a special key property, which it uses to identify them. React uses this to track which components it can remove, add, or change efficiently.
    Note

    The code references page.id. The pages returned by the backend should have the id, title, and body properties for these examples to work.

Let's concentrate on how to show each page through the content management system. The PageAdmin renders each page as a list item, so let's think about what we want to do inside each list item. I think it makes sense to have a non-interactive summary of each page. Think of a tabular view of all pages in a website:

  • Home
  • Products
  • Terms of service
  • Contact us

So there's one aspect to pages that is static: the view of the page title. Perhaps we can also include links to edit or delete each page.

We also want to be able to update each page. We're probably going to need some sort of form, with text inputs for each field we might want to update.

We can represent these two scenarios in a single component:

import React from "react";

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

        this.state = {
 "isEditing": false
 };
    }

    render() {
 if (this.state.isEditing) {
 return <PageEditor />;
 }

 return <PageView />;
    }
}

export default Page;

Now, we can switch between the different components, based on whether we're editing or not. Of course, we also need to define these new components:

import React from "react";

class PageEditor extends React.Component {
    render() {
        return <form>
            <input type="text" name="title" />
            <textarea name="body"></textarea>
            <button>back</button>
        </form>;
    }
}

export default PageEditor;

Note that we can define input elements in a way you might expect, if you've worked with HTML markup before. We'll revisit this component later, so don't worry about the details just yet.

The preview mode, for this component, is a little similar:

import React from "react";

class PageView extends React.Component {
    render() {
        return <p>
            {this.props.title}
            <button>edit</button>
            <button>delete</button>
        </p>;
    }
}

export default PageView;

This raises an interesting question. How can we efficiently transfer properties from one component to another? ES6 provides a great tool for this in the form of a language feature called the spread operator. First, we need to provide pages to page components in PageAdmin:

render() {
    return <ol>
        {this.state.pages.map(function(page) {
            return <li key={page.id}>
                <Page {...page} />
            </li>;
        })}
    </ol>;
}

We're replacing a new page with the Page component we created earlier. We use the spread operator to assign each object key as a component property. We can repeat this concept in Page:

render() {
    if (this.state.isEditing) {
        return <PageEditor {...this.props} />;
    }

    return <PageView {...this.props} />;
}

The {...this.props} expands the page object keys. The page.id becomes this.props.id inside the PageEditor and PageView components. This method is great for transferring many properties; we don't need to write out each one.

主站蜘蛛池模板: 太白县| 大理市| 丰顺县| 栾川县| 德保县| 赤水市| 南澳县| 黔西县| 沾益县| 台东县| 昌都县| 岳普湖县| 杨浦区| 苍南县| 双鸭山市| 漳平市| 白山市| 乌鲁木齐县| 天长市| 郎溪县| 五常市| 吐鲁番市| 公安县| 射阳县| 措勤县| 株洲市| 福鼎市| 潼关县| 仙居县| 义马市| 兴城市| 元阳县| 九寨沟县| 龙井市| 阳朔县| 都安| 怀宁县| 汶川县| 大化| 博客| 建昌县|