react components - sample chapter

Upload: packt-publishing

Post on 13-Apr-2018

222 views

Category:

Documents


0 download

TRANSCRIPT

  • 7/26/2019 React Components - Sample Chapter

    1/16

    C o m m u n i t y E x p e r i e n c e D i s t i l l e d

    Explore the power of React components for cutting-edge

    web development

    React Components

    Christopher Pitt

  • 7/26/2019 React Components - Sample Chapter

    2/16

    In this package, you will find: The author biography

    A preview chapter from the book, Chapter 3 'Saving and Communicating Data'

    A synopsis of the books content More information on React Components

  • 7/26/2019 React Components - Sample Chapter

    3/16

    About the Author

    Christopher Pittis a principal developer for SilverStripe in Wellington,New Zealand. He usually works on open source software, though sometimesyou'll find him building compilers and robots.

  • 7/26/2019 React Components - Sample Chapter

    4/16

    PrefaceReact is a fascinating new take on traditional frontend development. It has taken the

    JavaScript community by storm and has inspired sweeping changes in a number ofexisting JavaScript application frameworks and architectures.

    Unfortunately, there still aren't many examples of great architecture. Most tutorialsand books focus on small components and examples, leaving the question of largerapplications and component hierarchies unanswered. That is what this book seeksto change.

    What this book coversChapter 1, Thinking in Components, looks at the need to think of entire interfacesas a composition of small components and how to build them using modernES6 JavaScript.

    Chapter 2, Working with Properties and State, takes a comprehensive look at manyaspects of property and state management, sharing a few more ES6 tricks alongthe way.

    Chapter 3, Saving and Communicating Data, looks at reactive programming usingevent emitters and unidirectional flow of data.

    Chapter 4, Styling and Animating Components, takes a look at how components canbe styled and animated both inline and using stylesheets.

    Chapter 5, Going Material!, explores material design and applies what you learn to

    our set of components.Chapter 6, Changing Views, looks at ways of transitioning between different viewswith routing and animation.

  • 7/26/2019 React Components - Sample Chapter

    5/16

    Preface

    Chapter 7, Rendering on the Server, takes a look at the process of rendering componentsthrough nodes and some ways of structuring server-side application code.

    Chapter 8, React Design Patterns, explores different architectures such as Flux and Redux.

    Chapter 9, Thinking of Plugins, looks at how to build components with dependencyinjection and extension points.

    Chapter 10, Testing Components, explores various ways of ensuring that componentsare error-free and that changes to parts of an application don't have cascading effectson other parts.

  • 7/26/2019 React Components - Sample Chapter

    6/16

    [33 ]

    Saving and

    Communicating DataIn the previous chapter, we created complex component hierarchies. We created a list

    of pages and a way to edit those pages. Yet we stopped short of saving and readingany of that data to some kind of storage.

    We could, for instance, send an edit through an Ajax request to be saved in a databaseserver. In fact, that's what often happens in the applications we use these days. Theyalways save our interactions, irrespective of whether we expect them to or not.

    In this chapter, you will learn about local data stores and communicating withthem. You'll also learn about event-based architecture and how it promotes theunidirectional flow of data.

    There are many ways to save data. It's a rich and interesting topic that could fill

    scores of books. I could go so far as to say it is at the core of how businesses andapplications work.

    Furthermore, how data is communicated can often be different in a maintainableapplication and an unmaintainable application. It's up to us to figure out elegantways of persisting data so that our applications remain maintainable.

    We will only explore local storage in this chapter. You'll be able to see your stored databeyond page reloads, but nobody else will. You cannot build a practical website basedon this chapter alone. You will have to wait until we explore React on the server.

  • 7/26/2019 React Components - Sample Chapter

    7/16

    Saving and Communicating Data

    [34 ]

    Validating propertiesBefore we look at storing data, there is another habit I'd like to share with you.The components we created in the last chapter work well together, but our aimis to make each component self-contained. We want others to be able to reuse ourcomponents, but they will encounter problems if they don't know which properties

    our components expect.

    Consider what would happen if we used PageAdminlike this:

    ReactDOM.render(

    ,

    document.querySelector(".react")

    );

    Faced with this component, and no documentation, it might be tempting to substitute aBackendobject with some other configuration data. This looks reasonable to someoneunfamiliar with the component. And, without a careful study of all our components,

    we can't expect others to know what those properties should be.

    We can protect against this situation by adding property validation. Let's add somevalidation to PageEditor:

    PageEditor.propTypes = {

    "id": React.PropTypes.number.isRequired,

    "title": React.PropTypes.string.isRequired,

    "body": React.PropTypes.string.isRequired,

    "onUpdate": React.PropTypes.func.isRequired,

    "onCancel": React.PropTypes.func.isRequired

    };

    We have already imported the Reactobject, which exposes a PropTypesobject. Thiscontains some validators. When we specify a few on PageEditor.propTypes, Reactchecks the types of properties given to the component as it is rendered. If we givethe incorrect property types or omit required properties, React will emit a warning.

    The warnings look like this:

  • 7/26/2019 React Components - Sample Chapter

    8/16

    Chapter 3

    [35 ]

    There are many types to choose from, the simple ones being the following:

    React.PropTypes.array

    React.PropTypes.bool

    React.PropTypes.func

    React.PropTypes.number

    React.PropTypes.object

    React.PropTypes.string

    If you need a property to be required (which is likely in most cases) then you canadd .isRequiredat the end. Let's follow this up with validators for PageView:

    PageView.propTypes = {

    "title": React.PropTypes.string.isRequired,

    "onEdit": React.PropTypes.func.isRequired,

    "onDelete": React.PropTypes.func.isRequired

    };

    This is even simpler, given that PageViewuses fewer properties than PageEditor.Also, Pageis relatively simple:

    Page.propTypes = {

    "id": React.PropTypes.number.isRequired,

    "onDelete": React.PropTypes.func.isRequired

    };

    We don't need to validate properties passed straight through components.For instance, PageEditoruses onUpdate. It's passed through Page, but Pagedoesn't use it, PageEditordoes, so that's where we use validators for it.

    However, what if we want to validate nested structures or more complex types?We can try the following:

    PageAdmin.propTypes = {

    "backend": function(props, propName, componentName) {

    if (props.backend instanceof Backend) {

    return;

    }

    return new Error(

    "Required prop `backend` is not a `Backend`." );

    }

    };

  • 7/26/2019 React Components - Sample Chapter

    9/16

    Saving and Communicating Data

    [36 ]

    We expect the backendproperty to be an instance of the Backendclass. If it is anythingelse, we return an Errordescribing why the property is invalid. We can also use shapeto validate nested properties:

    Component.propTypes = {

    "page": React.PropTypes.shape({

    "id": React.PropTypes.number.isRequired,

    "title": React.PropTypes.string.isRequired,

    "body": React.PropTypes.string.isRequired

    })

    };

    The more specific we are about properties, the less chance there is for bad propertiesto break the interface. So, it's good to get in the habit of defining them all the time.

    Storing cookiesYou must have heard of cookies before. They're a browser-based storage mechanismas old as the Internet, and they are often comically described in movies. Here's howwe use them:

    document.cookie = "pages=all_the_pages";

    document.cookie = "current=current_page_id";

    The document.cookieparameter works as a temporary string store. You can keepadding new strings, where the key and value are separated by =, and they will bestored beyond a page reload, that is, until you reach the limit of how many cookiesyour browser will store per domain. If you set document.cookiemultiple times,multiple cookies will be set.

    You can read the cookies back again, with a function like this:

    var cookies = {};

    function readCookie(name) { var chunks = document.cookie.split("; ");

    for (var i = chunks.length - 1; i >= 0; i--) { var parts = chunks[i].split("="); cookies[parts[0]] = parts[1]; }

    return cookies[name];}

    export default readCookie;

  • 7/26/2019 React Components - Sample Chapter

    10/16

    Chapter 3

    [37 ]

    The whole cookie string is read and split using semicolons. Then, each cookie is splitinto equals, leaving the key and value. These are stored in the local cookiesobject.Future requests just read the key from the local object. The cookiesobject can beinspected at any point to see the cookies that have been set.

    Try http://browsercookielimits.squawky.netto test what your browser can

    handle. I'm running a modern version of Chrome, and I can probably store 180cookies per domain, totaling 4096 bytes. 4096 bytes doesn't sound like a lot...

    Cookies aren't typically used for the kinds of data we want to store. We'll have tolook elsewhere.

    If you want to learn more about how to use cookies, head over tohttps://developer.mozilla.org/en-US/docs/Web/API/Document/cookie.

    Using local storageThe next type of storage we will look at is a relatively recent addition to the browsertoolset. It's called local storage, and it's been around for a while. You can add items toit as follows:

    localStorage.setItem("pages", "all_the_pages");

    It's simpler than cookies to read items from:

    localStorage.getItem("pages");

    This will persist the data beyond page reloads or the browser closing. You can storeconsiderably more data than in cookies (anywhere from 3 MB to 10 MB, by default),and the interface is easier to use.

    So, how can we use this to store our pages? Let's abstract local storage a bit:

    export default {

    "get": function(key, defaultValue) {

    var value = window.localStorage.getItem(key);

    var decoded = JSON.parse(value);

    if (decoded) { return decoded;

    }

  • 7/26/2019 React Components - Sample Chapter

    11/16

    Saving and Communicating Data

    [38 ]

    return defaultValue;

    },

    "set": function(key, value) {

    window.localStorage.setItem(

    key, JSON.stringify(value)

    ); }

    };

    For once, we're exporting an object instead of a class. This object has a couple ofmethods both of which access window.localStorage. It's not ideal to referencethis directly, but if we use this abstraction everywhere else, then I think it's OK.

    The getmethod pulls a string value out of local storage and parses it as a JSONstring. If the value parses to any non-false value, we return it, or else we return adefault value.

    The setmethod encodes a value as JSON, and stores it.

    Then, we can use the following abstraction in the Backendclass:

    import LocalStore from "local-store";

    class Backend {

    constructor() {

    this.pages = LocalStore.get("pages", []);

    }

    getAll() {

    return this.pages;

    }

    update(id, property, value) {

    this.pages = this.pages.map((page) => {

    if (page.id == id) {

    page[property] = value;

    }

    return page;

    });

    LocalStore.set("pages", this.pages);

    }

  • 7/26/2019 React Components - Sample Chapter

    12/16

    Chapter 3

    [39 ]

    delete(id) {

    this.pages = this.pages.filter(

    (page) => page.id !== id

    );

    LocalStore.set("pages", this.pages);

    }}

    export default Backend;

    We begin with a constructor that fetches any stored pages from localStorage. Weprovide a default empty array in case the pageskey is missing in localStorage.We store that in this.pagesso we can fetch and modify it later.

    The getAllmethod is much simpler this time around. All it does is returns this.pages. The updateand deletemethods become more interesting though. Theupdatemethod uses theArray.mapmethod to apply updates to the affected pageobjects. We have to store the updated pagesarray back in local storage so that thechanges are persisted.

    Similarly, deletemodifies the pagesarray (this time with a short function syntax)and stores the modified array back in local storage. We have to see local storage withsome initial data. You can do this in a developer console:

    localStorage.setItem("pages", JSON.stringify([

    {

    "id": 1,

    "title": "Home",

    "body": "..." },

    {

    "id": 2,

    "title": "About Us",

    "body": "..."

    },

    {

    "id": 3,

    "title": "Contact Us",

    "body": "..."

    }, {

    "id": 4,

    "title": "Products",

  • 7/26/2019 React Components - Sample Chapter

    13/16

    Saving and Communicating Data

    [40 ]

    "body": "..."

    }

    ]));

    If you've made these changes, and you refresh the page, you should see the newbackend code in action!

    Using event emittersUntil now, our components have communicated with the backend through methodcalls. That's OK for tiny applications, but when things start to scale, we will forget tomake some of those method calls.

    Look at onUpdate, for instance:

    onUpdate(id, field, value) {

    this.props.backend.update(id, field, value);

    this.setState({

    "pages": this.props.backend.getAll()

    });

    }

    Every time we change the state of a page, we have to fetch an updated list of pagesfrom the backend. What if multiple components send updates to the backend?How will our PageAdmincomponent know when to fetch a new list of pages?

    We can turn to event-based architecture to solve this problem. We've alreadyencountered and used events! Recollect what we did when we created the page

    edit form. There, we connected to input events so we could update pages wheninput values changed.

    This kind of architecture moves us closer to a unidirectional flow of data. We canimagine our entire application like a tree of components, beginning with a singleroot component. When a component needs to update some application's state, wedon't need to code the state change in relation to where that component is. In thepast, we may have had to reference specific CSS selectors, or depend on the positionof sibling elements, when updating state.

    When we start to use events, then any component can trigger a change in the

    application. Also, multiple components can trigger the same kind of change.We'll explore this idea in more detail in later chapters.

  • 7/26/2019 React Components - Sample Chapter

    14/16

    Chapter 3

    [41 ]

    We can use that same idea to notify components when the data changes. To beginwith, we need to download an event emitter class:

    $ npm install --save eventemitter3

    Now, Backendcan extend this, providing the event functionality that we are after:

    class Backend extends EventEmitter { constructor() {

    super();

    this.pages = LocalStore.get("pages", []);

    }

    getAll() {

    return this.pages;

    }

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

    this.emit("update", this.pages);

    }

    delete(id) {

    // ...delete a page

    this.emit("update", this.pages);

    }

    }

    As each page is updated or deleted, the backend will emit an event on itself. Thisdoes nothing until we listen for these events in PageAdmin:

    constructor(props) {

    super(props);

    this.bind(

    "onUpdate",

    "onDelete"

    );

    this.state = {

    "pages": this.props.backend.getAll()

    };

  • 7/26/2019 React Components - Sample Chapter

    15/16

    Saving and Communicating Data

    [42 ]

    this.props.backend.on("update",

    (pages) => this.setState({pages})

    );

    }

    onUpdate(id, field, value) {

    this.props.backend.update(id, field, value);}

    onDelete(id) {

    this.props.backend.delete(id);

    }

    Now we can remove the numerous calls to this.setStateand replace them witha single event listener in the constructor. We are also doing something interestingwith the setStatecall. It's called object destructuring, and it allows {pages}tobecome {"pages":pages}.

    Now we can begin to use this backend for many different parts of the interface, andthey'll all have accurate, real-time data. Open the page up in a few different windowsand watch them all update at once!

    SummaryIn this chapter, we looked at how to protect our components from faulty properties.We also saw how easy it was to use cookies, although they are limited for what weneed. Fortunately, we can use local storage and work it into our existing backendand components.

    Finally, we explored using events to push state changes out to all interestedcomponents.

    In the next chapter, we will start prettying up our components. We'll look at ways tostyle and animate them, bringing our interface to life!

  • 7/26/2019 React Components - Sample Chapter

    16/16

    Where to buy this bookYou can buy React Components from thePackt Publishing website.

    Alternatively, you can buy the book from Amazon, BN.com, Computer Manuals and most internetbook retailers.

    Click herefor ordering and shipping details.

    www.PacktPub.com

    Stay Connected:

    Get more information React Components

    https://www.packtpub.com/web-development/react-components/?utm_source=scribd&utm_medium=cd&utm_campaign=samplechapterhttps://www.packtpub.com/web-development/react-components/?utm_source=scribd&utm_medium=cd&utm_campaign=samplechapterhttps://www.packtpub.com/web-development/react-components/?utm_source=scribd&utm_medium=cd&utm_campaign=samplechapterhttps://www.packtpub.com/books/info/packt/ordering/?utm_source=scribd&utm_medium=cd&utm_campaign=samplechapterhttps://www.packtpub.com/books/info/packt/ordering/?utm_source=scribd&utm_medium=cd&utm_campaign=samplechapterhttps://www.packtpub.com/?utm_source=scribd&utm_medium=cd&utm_campaign=samplechapterhttps://www.packtpub.com/?utm_source=scribd&utm_medium=cd&utm_campaign=samplechapterhttps://www.packtpub.com/web-development/react-components/?utm_source=scribd&utm_medium=cd&utm_campaign=samplechapterhttps://www.packtpub.com/web-development/react-components/?utm_source=scribd&utm_medium=cd&utm_campaign=samplechapterhttps://www.packtpub.com/web-development/react-components/?utm_source=scribd&utm_medium=cd&utm_campaign=samplechapterhttps://www.linkedin.com/company/packt-publishinghttps://plus.google.com/+packtpublishinghttps://www.facebook.com/PacktPub/https://twitter.com/PacktPubhttps://www.packtpub.com/?utm_source=scribd&utm_medium=cd&utm_campaign=samplechapterhttps://www.packtpub.com/books/info/packt/ordering/?utm_source=scribd&utm_medium=cd&utm_campaign=samplechapterhttps://www.packtpub.com/web-development/react-components/?utm_source=scribd&utm_medium=cd&utm_campaign=samplechapter