Skip to content

Latest commit

 

History

History
186 lines (132 loc) · 6.62 KB

4 - Props.md

File metadata and controls

186 lines (132 loc) · 6.62 KB

4. Props

As it stands, our <Post/> components don't have any real information in them. Everything is either empty or dummy values. How would our <Post/>s get that information? That information would have to be passed down from the <Posts/>s' parent component: <App/>. Values passed from parent components to children components are called props.

Passing Down Props

Props are passed down using JSX's XML-like syntax. For instance, if <MyParentComponent/> wants to pass a foo prop with a value of 5 to its <MyChildComponent/>, then it could call

<MyChildComponent foo=5 />

If instead <MyParentComponent/> wants to pass the contents of the num variable as the foo prop, it could use JSX's curly bracket syntax:

<MyChildComponent foo={num} />

What information do our <Post/> components need from <App/>? They need the URL for their posters' avatar, they need their posters' name, they need their posters' unique identifier so that the names can link to the posters' profile, and they need the actual contents of the status. That makes six props:

  • key, which is required whenever you're injecting a list of child components, but that we as developers don't actually have access to

  • id, which is our own version of the post's unique identifier

  • avatar, which represents the poster's avatar URL

  • name, which represents the poster's name

  • user_id, which could -- in a more complete app -- be used to navigate to the poster's profile page

  • status, which represents the post contents

Let's add these props in app.js:

render () {
    let feed = [];

    for(var i = 0; i < list.length; i++) {
        let post = <Post key={i} id={i} avatar={list[i].avatar} user_id={list[i].user_id}
            name={list[i].name} status={list[i].status}/>

        feed.push(post);
    }

    return feed;
}

Using Props Inside the Component

Now that <App/> has given each <Post/> their props, we can use those inside the <Post/> component.

If a component is given a foo prop, the component can access it by calling this.props.foo.

NOTE: In React, a component's props are immutable. In other words, once they're set, they can't be changed. The component must instead be replaced by another instance of the same component, just with modified props.

Using this, let's start to implement our props in the <Post/> component. Since we're injecting the props' values into our JSX, we'll have to use curly brackets.

post.js

render() {
    return (
        <div>
            <img src={this.props.avatar}/>
            <h1><a href={`./${this.props.user_id}`}>{this.props.name}</a></h1>
            <p>{this.props.status}</p>
        </div>
    );
}

Run npm run webpack and refresh. With any luck, each post should now have the actual contents of our JSON file. Clicking on each user's name should take you to a separate (and not yet implemented) page based on their user_id. The images should be functional as well.


Applying CSS in JSX

One of the files given to you in the boilerplate was style.css, which has already been imported into index.html. style.css defines a .post-box rule that might help make each post its own distinct-looking component:

style.css

.post-box {
    box-shadow: 0px 0px 20px gray;
    margin: 10px;
    padding: 10px;
    font-family: sans-serif;
}

We can apply this to our <div> inside <Post/>'s render() function:

render() {
    return (
        <div>
            <img src={this.props.avatar} className="post-box"/>
            <h1><a href={`./${this.props.user_id}`}>{this.props.name}</a></h1>
            <p>{this.props.status}</p>
        </div>
    );
}

Notice that we used the className attribute. JSX is not exactly identical to HTML. Here's why the decision to use className over class was used.

Those avatars are pretty big, aren't they? It'd be nice if we could make them much smaller, and make them inline to the users' names.

render() {
    return (
        <div className="post-box">
            <img src={this.props.avatar} style={{width: 40, height: 40, display: "inline-block"}}/>
            <h1 style={{display: "inline-block"}}><a href={`./${this.props.user_id}`}>{this.props.name}</a></h1>
            <p>{this.props.status}</p>
            <hr/>
            <textarea id="write-comment" className="textarea"/>
            <button>Comment</button>
        </div>
    );
}

Notice how JSX treats the style attribute instead of using a string like HTML would. The doubled-up curly braces indicate that this is a JSON object (inner braces) being injected into the JSX (outer braces). Each style attribute is a key-value pair in that style object. Every CSS attribute that would have included dashes (like margin-top, for instance) is instead represented in camelcase (marginTop).

Run npm run webpack and revel in a slightly better looking news feed!

State of the Code

app.js

var React = require('react');
var ReactDOM = require('react-dom');
var list = require('./list.json');
var Post = require('./post');

class App extends React.Component {
    render () {
        let feed = [];

        for(var i = 0; i < list.length; i++) {
            let post = <Post key={i} avatar={list[i].avatar} user_id={list[i].user_id}
                name={list[i].name} status={list[i].status}/>

            feed.push(post);
        }

        return feed;
    }
};

ReactDOM.render(<App/>, document.getElementById("app"));

post.js

import React from 'react';

class Post extends React.Component {

    render() {
        return (
            <div className="post-box">
                <img src={this.props.avatar} style={{width: 40, height: 40, display: "inline-block"}}/>
                <h1 style={{display: "inline-block"}}><a href={`./${this.props.user_id}`}>{this.props.name}</a></h1>
                <p>{this.props.status}</p>
                <hr/>
                <textarea id="write-comment" className="textarea"/>
                <button>Comment</button>
            </div>
        );
    }
}

module.exports = Post;