BUilding Reactive Applications with Meteor and React

Ramsay Lanier

I LOVE JAVASCRIPT

ON THE CLIENT

ON THE server

in a box

with a fox

client

+

SERVER

=

ISOMORPHIC

javascript everywhere

so what is meteor?

"Meteor is a full-stack JavaScript App Platform that assemble all the pieces you need to build modern web and mobile apps, with a single JavaScript codebase."

library of packages: pre-written, self-contained modules that you might need in your app.

the seven principles of meteor

1. Data on the wire

Meteor doesn't send HTML over the network. The server sends data and lets the client render it.

(you can, with a little bit of effort, have server side rendering though.)

2. one language

Isomorphic

3. Database everywhere

You can use the same methods to access your database from the client or server, thanks to minimongo.
 

4. latency compensation

Client-side, Meteor prefetches data and simulates models to make it look like a server method call returns instantly.

We call this optomistic UI.
 

5. full stack reactivity

Everything gets updates automatically, in real-time, and only when necessary.
 

6. embrace the ecosystem

Meteor is open source and it should be able to integrate with other open source platforms.

Like React. 

7. simplicity = productivity

Clean, easy to use APIs.

meteor on the server

DDP

MongoDB

hammer TIME

built with Meteor and React and Giphy and parachute pants.

REACT

// /lib/router.jsx 

FlowRouter.route('/', {
    action: function(){
        ReactLayout.render(MainLayout, {
	    content: <HammerPage/>
	})
    }
})

// client/components/Layout/mainLayout.jsx

MainLayout = React.createClass({
    render(){

        return (
	    <div className="application">
                <Header className="app-header">
		    <div className="title-container">
			<p className="title"></p>
		    </div>
		</Header>

		<main>
		    {this.props.content}
		</main>
	    </div>
        )
    }
});

REACT

// /client/components/Page/_hammerPage.jsx

HammerPage = React.createClass({

    mixins: [ReactMeteorData],

    getMeteorData(){
        let subscription = Meteor.subscribe('hammerList');

        return {
	    loading: !subscription.ready(),
	    hammers: Hammers.find({}, {sort: {createdOn: -1}}).fetch()
        };
    },

    render(){
        if (this.data.loading){
	    return <p>...loading</p>
	} else{
	    return (
                <Page>
                    <HammerButton />
		    <HammerList hammers={this.data.hammers}/>
		</Page>
	    )
	}
    }
});
// /server/publications.js

Meteor.publish('hammerList', function(){
    return Hammers.find();
})

REACT

// /client/components/Page/page.jsx

Page = React.createClass({
    componentDidMount(){
        //animate Page on load
        let item = $(this.getDOMNode());
        TweenMax.fromTo(page, .5, {
	    opacity: 0
	}, {
	    opacity: 1,
	    ease: Power4.easeOut
	} )
    },

    render(){
        let styles = {
            backgroundImage: "url('" + this.props.backgroundImage + "')",
        }

	let children = this.props.children;
	    return(
	        <div className={"page " + this.props.className} style={styles}>
		    {children}
		</div>
	    )
	}
});

REACT

// /client/components/Button/_hammerButton.jsx

HammerButton = new React.createClass({

  render(){
    return(
      <div className="hammer-button">
        <input type="text" className="hammer-name-field" placeholder="Name Your Hammer"/>
        <Button action={this.action} className="primary-btn">Add Hammer</Button>
      </div>
    )
  },

  action(){

    // use the meteor HTTP package to get a random MC Hammer GIF!
    HTTP.get('http://api.giphy.com/v1/gifs/random?api_key=dc6zaTOxFJmzC&tag=mc+hammer', function(error,result){

      let hammerImage = result ? result.data.data.image_url : '/images/hammertime.gif';
      let hammerName = $('.hammer-name-field').val();

      //call a Meteor Methods to create a new Hammer. This methods is available on both the client and the server! Latency Compensation FTW!
      Meteor.call('addHammer', hammerImage, hammerName, function(err, res){
        if (err){
          alert(err.reason)
        }
      });
    });
  }
})

REACT

// lib/collections/hammers.js

//this is our Hammers collection 
Hammers = new Mongo.Collection('hammers');

Meteor.methods({

  addHammer: function(hammerImage, hammerName){
    //we should see this logged to both the client and the server logs!
    console.log(hammerName);
    if (!hammerName){
      throw new Meteor.Error(422, 'Please name your Hammmmmmer.')
    }

    //uses Meteor's Check package to check that arguments are Strings, not Objects. Objects can be bad!
    check(hammerName, String);
    check(hammerImage, String);

    //create a new Hammer by inserting it into a Mongo DB collection. This returns the Mongo ID of the new document
    return Hammers.insert({image: hammerImage, name: hammerName, createdOn: new Date()});
  }
})

REACT

// client/components/List/_hammerList.jsx

HammerList = new React.createClass({

  render(){

    // this is passed in from HammerPage - we only get data from top level components and let it flow down.
    let hammers = this.props.hammers;

    //loop through each object in hammers and render the HammerItem component
    return (
      <List className="hammer-list">
        {hammers.map( hammer => {
          return(
            <HammerItem key={hammer._id} hammer={hammer} />
          )
        })}
      </List>
    )
  }
})


HammerItem = new React.createClass({
  componentDidMount(){
    //lets animate these when the get rendered!
    let count = Session.get('itemCount') || 0;
    Session.set('itemCount', count + 1);

    let item = $(React.findDOMNode(this.refs.item));
    let delay = (count % 6) / 20;

    TweenMax.fromTo(item, 1, {
      opacity: 0,
      y: 20
    }, {
      opacity: 1,
      y: 0,
      ease: Power4.easeInOut,
      delay: delay
    })
  },

  render(){
    let style = {
      backgroundImage: "url('" + this.props.hammer.image + "')"
    }

    return(
      <li ref="item" style={style} className="hammer-item">
        <h2 className="hammer-name">{this.props.hammer.name}</h2>
      </li>
    )
  }
})

Meteor Resources

QUESTIONS

Reactive Applications with Meteor and React

By Ramsay Lanier

Reactive Applications with Meteor and React

  • 1,506