Become comfortable with routing and lifecycle methods.
Before starting to code, browse through the provided components and become familiar with the structure. Each component is provided pre-built and will be modified throughout the project.
Begin by setting up imports, you will need access to the usual React and ReactDOM as well as the following:
Router,Route, andbrowserHistoryfrom React Router.- From the components directory:
AppInboxMessageDraftContacts
Render a router onto the div with the id of root. The router should:
- Implement
browserHistory - Have a root route to the path
"/"handled by theAppcomponent. - The root route should have three sub-routes of
"draft","contacts", and"inbox". Handled by the appropriate components. - The inbox route should have a sub-route with a parameter of
messageId, handled by theMessagecomponent.
Your code should look something like this:
// index.js
import React from "react";
import ReactDOM from "react-dom";
import { Router, Route, browserHistory } from "react-router";
import App from "./components/App";
import Inbox from "./components/Inbox";
import Message from "./components/Message"
import Draft from "./components/Draft";
import Contacts from "./components/Contacts";
document.addEventListener( "DOMContentLoaded", () => {
const reactNode = document.getElementById( "root" );
if ( reactNode ) {
ReactDOM.render(
<Router history={ browserHistory }>
<Route path="/" component={ App }>
<Route path="draft" component={ Draft } />
<Route path="contacts" component={ Contacts } />
<Route path="inbox" component={ Inbox }>
<Route path="/inbox/:messageId" component={ Message } />
</Route>
</Route>
</Router>
, reactNode )
}
} );Inside of your App component's render method, display its children beneath the header. Like this:
// App.js
render() {
return (
<div>
<Header />
{ this.props.children }
</div>
);
}Our router is now set up and ready for use, we just need to configure each individual component to get the data necessary and navigate the application.
Our Header component will serve as a navigation bar for the application, so we'll start here.
- Begin by importing the
Linkcomponent from React Router. - Use the Link component to wrap the
h1element with a link to the root route. - In the
divjust below theheaderclosing tag add links to inbox, draft, and contacts. - Wrap the text of the inbox, draft, and contacts links in
span's with styles ofstyles.link.
Your render method's JSX should look something like this:
<header style={ styles.wrapper }>
<div>
<img
alt="React Logo"
src="assets/react-logo.svg"
style={ styles.logoImage }
/>
<Link to="/"><h1 style={ styles.header }>rMail</h1></Link>
</div>
<div>
<Link to="/inbox">
<span style={ styles.link }>Inbox</span>
</Link>
<Link to="/draft">
<span style={ styles.link }>Draft</span>
</Link>
<Link to="/contacts">
<span style={ styles.link }>Contacts</span>
</Link>
</div>
</header>Our Contacts component will display a list of contacts provided by contactsService.
- Import
getContactsfromcontactsService. - Using a lifecycle method, get the contacts and place them on state before the component mounts.
- Create an array of
Contactcomponents. Each contact should be passed a company, email, name, and phone property. - Render the array of contacts inside of the
divwith the style ofcontactsWrapper. - Inside of the
Contactcomponent, simply update the commented out fields to display the correct data.
Your code should look something like this:
// Contacts.js
import React from "react";
import { getContacts } from "../services/contactsService";
import Contact from "./Contact";
export default class Contacts extends React.Component {
constructor( props ) {
super( props );
this.state = {
contacts: []
}
}
componentWillMount() {
this.setState( { contacts: getContacts() } );
}
render() {
const styles = this.getStyles();
const contacts = this.state.contacts.map( contact => (
<Contact
company={ contact.company }
email={ contact.email }
key={ contact._id }
name={ contact.name }
phone={ contact.phone }
/>
) );
return (
<div>
<h1>Contacts</h1>
<div style={ styles.contactsWrapper }>
{ contacts }
</div>
</div>
);
}
getStyles() {
return { /* */ }
}
}// Contact.js
import React from "react";
export default class Contact extends React.Component {
render() {
const styles = this.getStyles();
return (
<ul style={ styles.contactWrapper }>
<li style={ styles.name }>{ this.props.name }</li>
<li>Company: { this.props.company }</li>
<li>Email: { this.props.email }</li>
<li>: Phone #: { this.props.phone }</li>
</ul>
);
}
getStyles() {
return { /* */ }
}
}The draft component needs very little work. Simply add a sendMessage method that takes in an event and prevents the default HTML action, then re-directs the browser back to "/inbox" using browserHistory. This function should fire when the send button is clicked.
import React from "react";
import { browserHistory } from "react-router";
export default class Draft extends React.Component {
sendMessage( event ) {
event.preventDefault();
browserHistory.push( "/inbox" );
}
render() {
return (
// inputs and textarea.
<button
onClick={ this.sendMessage }
style={ styles.sendButton }
>
Send
</button>
// rest of render
}
// rest of classThe Inbox component will display a list of MessageLink components on the left, and its sub-route displaying the message content on the right.
- Display the component's children inside the
divwith the style ofactiveMessageWrapper. - Import
getMessagesfrommessageServiceand put the messages on state before the component mounts using a lifecycle method. - Import the
MessageLinkcomponent and create an array of message links.- Each JSX element in the array should be wrapped in a
Linktag pointing to inbox's sub-route, passing the message_idas the route parameter. - Each
MessageLinkshould be passed properties of email and name.
- Each JSX element in the array should be wrapped in a
- Render this array inside the
divwith the style ofmessageLinkWrapper. - Update the render method of
MessageLinkto display the proper data in place of the comments.
Your components should look something like this:
// Inbox.js
import React from "react";
import { Link } from "react-router";
import { getMessages } from "../services/messageService";
import MessageLink from "./MessageLink";
export default class Inbox extends React.Component {
constructor( props ) {
super( props );
this.state = { messages: [] };
}
componentWillMount() {
this.setState( { messages: getMessages() } );
}
render() {
const styles = this.getStyles();
const messages = this.state.messages.map( message => (
<Link
key={ message._id }
to={ `/inbox/${ message._id }` }
>
<MessageLink
email={ message.email }
key={ message._id }
name={ message.name }
/>
</Link>
) );
return (
<div style={ styles.wrapper }>
<div style={ styles.messageLinkWrapper }>
{ messages }
</div>
<div style={ styles.activeMessageWrapper }>
{ this.props.children }
</div>
</div>
);
}
getStyles() {
return { /* */ }
}
}// MessageLink.js - Just the render method JSX
<div style={ styles.wrapper }>
<h3>{ this.props.name }</h3>
{ this.props.email }
</div>Our message component will make use of lifecycle methods to ensure its data is up to date and displaying properly.
- Update the commented out sections of the render method to look to state for their values.
- Import
findMessageByIdfrommessageService. - Create a new method named
getMessagethat takes in amessageIdparameter, finds a message by ID, and places that message on state. - Using a lifecycle method, call your
getMessagemethod before the component mounts. - Using a lifecycle method, determine whether the component needs to re-call
getMessage. Look to the route params to determine this.
Your code should look something like this:
import React from "react";
import { findMessageById } from "../services/messageService";
export default class Message extends React.Component {
constructor( props ) {
super( props );
this.state = {
name: ""
, email: ""
, content: ""
}
}
componentWillMount() {
this.getMessage.call( this, this.props.params.messageId );
}
componentWillUpdate( nextProps, nextState ) {
if ( nextProps.params.messageId !== this.props.params.messageId ) {
this.getMessage.call( this, nextProps.params.messageId );
}
}
getMessage( messageId ) {
const message = findMessageById( messageId );
this.setState( {
name: message.name
, email: message.email
, content: message.content
} );
}
render() {
return (
<div>
<h1>{ this.state.name }</h1>
<h3>{ this.state.email }</h3>
<p>{ this.state.content }</p>
</div>
);
}
}- Turn each
Contactcomponent into a link that will take you to a new route that displays the individual contact's data. - Update the
Draftcomponent so that messages are saved toApp's state when the send button is clicked, and add a route to view sent messages. - Move the data from
messageServiceandcontactsServiceinto a simple node API or JSON file and use axios or superagent to fetch the data via HTTP.
If you see a problem or a typo, please fork, make the necessary changes, and create a pull request so we can review your changes and merge them into the master repo and branch.
© DevMountain LLC, 2017. Unauthorized use and/or duplication of this material without express and written permission from DevMountain, LLC is strictly prohibited. Excerpts and links may be used, provided that full and clear credit is given to DevMountain with appropriate and specific direction to the original content.
