In this tutorial I’ll be showing you how to create a very Twitter Clone using React and Firebase. MobX will be used as the data store for Firebase data. Here’s what the final output is going to look like:

Firebase Setup

In order to not bog us down in Firebase setup, this tutorial will assume that you already have an existing Firebase project created in the Firebase Console. If not, go ahead and create one. Once you have a project, go to the database section and under the rules tab add the following:

{ "rules": { ".read": true, ".write": true } }

This sets read and write permissions of your database to public. This means you don’t need to authenticate later on in order to gain access the database. This is good for quickly getting started with something, but not for production apps. Be sure to change this later!

Project Setup

Clone the create-react-app-mobx project. This allows you to easily create a React Project which has already the MobX toolset built into it (the original create-react-app doesn’t support all MobX features yet).

git clone https://github.com/mobxjs/create-react-app-mobx.git react-firebase-mobx-twitter-clone

Next, open your package.json file and add the following:

"mobx-firebase-store": "^1.0.3", "react-timeago": "^3.2.0", "chance": "^1.0.6", "slugify": "^1.1.0", "twitter-text": "^1.14.3"

Here’s a breakdown of what each package does:

mobx-firebase-store – used for storing Firebase data in MobX maps. This uses Firebase-nest as a peer dependency to subscribe to changes in a Firebase store.

– used for storing Firebase data in MobX maps. This uses Firebase-nest as a peer dependency to subscribe to changes in a Firebase store. react-timeago – used for generating a human-friendly text based on a timestamp.

– used for generating a human-friendly text based on a timestamp. chance – used for generating a random name to be assigned to the current user.

– used for generating a random name to be assigned to the current user. slugify – used for making the random name URL-friendly.

– used for making the random name URL-friendly. twitter-text – contains utility functions for working with twitter text. Things like counting the remaining text and converting URL’s into links.

Execute npm install to install all the packages. Once that’s done, you can run npm start to serve the project for development. You can access https://localhost:3000 on your browser to view the project.

Creating the Main Component

Before moving on, delete everything inside the src folder of the project. By default it contains a demo project that uses MobX to implement a counter app. We don’t really need those so you can go ahead and delete them.

Create an index.js file. This is where you supply your Firebase database URL and render the main app component which you’ll be creating later:

import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; const config = { databaseURL: 'https://{YOUR_FIREBASE_PROJECT_NAME}.firebaseio.com', }; ReactDOM.render( , document.getElementById('root') );

Next, create an App.js file. This is where you import the TweetList component we’ll build that serves as the component that the user will interact with, and the TweetStore store which contains the code for connecting, and saving/reading data from Firebase.

import React, { Component } from 'react'; import TweetList from './TweetList'; import TweetStore from './TweetStore'; import './index.css'; class App extends Component { componentWillMount() { this.store = new TweetStore(this.props.config); //create a new instance of the store by passingin the Firebase config } render() { return ( MobX-Firebase Twitter Clone

Creating the TweetList Component

Now we’ll create the TweetList.js file. At the very top, import all the libraries and components that you need. You’ll see how each of these will be used later so we won’t cover what each one does here.

import React, { Component } from 'react'; import { observer } from 'mobx-react'; import { createAutoSubscriber } from 'firebase-nest'; import Chance from 'chance'; import slugify from 'slugify'; import TimeAgo from 'react-timeago' import twitter_text from 'twitter-text'; const tweet_limit = 140; var ch = new Chance();

Next, create the Tweetlist component. Initialize the state as well as the functions inside the constructor .

class TweetList extends Component { constructor(props) { super(props); this.state = { tweet: '', //the user's tweet text username: slugify(ch.name()), loading: false, //whether the Firebase data is currently loading or not remaining: tweet_limit //number of characters allowed for each tweet }; this.updateText = this.updateText.bind(this); this.submitTweet = this.submitTweet.bind(this); } ... }

The render() method uses the getTweets() method from the store. You’ll be creating this later, for now know that everytime someone makes a change to your Firebase database, the component gets re-rendered. If the Firebase data becomes available, the textarea for entering the tweet and the list of tweets gets rendered.

render() { const tweets = this.props.store.getTweets(); if (!tweets) { return Loading tweets... } return ( {this.state.username} {this.state.tweet} {this.state.remaining} tweet {tweets.keys().reverse().map(messageKey => this.renderTweet(messageKey, tweets.get(messageKey)))}

Here’s the function for rendering each tweet:

renderTweet(key, tweet) { return ( @{tweet.username} - ); }

The function for updating the state of the current tweet text and remaining count:

updateText(evt) { let tweet = evt.target.value let remaining = tweet_limit - twitter_text.getTweetLength(tweet); this.setState({ tweet, remaining }); }

The function for saving the tweet in Firebase:

submitTweet() { this.props.store.createTweet({ username: this.state.username, timestamp: new Date().getTime(), text: this.state.tweet }); this.setState({ tweet: '', remaining: tweet_limit }); }

Next, you need to define the getSubs() and subscribeSubs() function. The naming is relevant because the Firebase-nest library rely on these two functions. getSubs() returns the array of subscriptions while subscribeSubs() performs the subscription and returns the function for unsubscribing from the Firebase data.

getSubs(props, state) { return props.store.allTweetsSubs(); } subscribeSubs (subs, props, state) { return props.store.subscribeSubs(subs); }

Finally, you use the createAutoSubscriber() function to subscribe and observe to Firebase data. This function uses the getSubs() and subscribeSubs() functions that you defined earlier in order to subscribe to Firebase data and propagate the changes in the store. It will then return the function that requires a Reactive component to be passed in. We can turn our component into a Reactive one by wrapping it in MobX’s observer() function. This ensures that any data that is used for rendering the component forces a re-rendering when it’s updated. If you don’t set your component as an observer, it won’t re-render when the current user or someone else posts a new tweet.

export default createAutoSubscriber()(observer(TweetList));

Creating the Store

We shall now create out Mobx store. Create a TweetStore.js file. This serves as the store for the TweetList component. It contains the methods for saving and fetching data from Firebase.

Start by importing firebase and mobx-firebase-store . firebase is the official Firebase JavaScript library for working with Firebase. This allows you to connect to Firebase. The mobx-firebase-store library provides a bridge between the Firebase library and the MobX library. It allows you to subscribe to Firebase data through the use of firebase-nest subscriptions. This makes the data flow into MobX’s observable maps.

import Firebase from 'firebase'; import MobxFirebaseStore from 'mobx-firebase-store';

Define the property in which the array of tweets are stored:

const tweets_subkey = 'tweets';

Here’s what it looks like in the Firebase console:

Create the store. Inside the constructor , connect to Firebase and create the store. MobXFirebaseStore accepts a reference to a Firebase database as its argument. Right below that, call super(store.fb) to make the store available inside the class via this .

export default class TweetStore extends MobxFirebaseStore { constructor(config) { const fbApp = Firebase.initializeApp(config); const store = new MobxFirebaseStore(Firebase.database(fbApp).ref()); super(store.fb); } ... }

allTweetsSubs() as you’ve known earlier, returns an array of subscriptions. In this case you only want to subscribe to a single subkey ( tweets ) and you want its data to be returned as a list ( asList: true ). Basically, it’s used to specify the subkey in your Firebase database that you want to subscribe to.

allTweetsSubs() { return [{ subKey: tweets_subkey, asList: true }]; }

createTweet() uses Firebase’s push method to push the tweet object into the array. This creates a new item in the tweets array in Firebase which then triggers the subscriber to update the list of tweets for all the connected clients.

createTweet(tweet) { this.fb.child(tweets_subkey).push(tweet); }

getTweets() returns the collection of tweets:

getTweets() { return this.getData(tweets_subkey); }

resolveFirebaseQuery() is used for customizing the query used for fetching Firebase data. Here, the results are ordered according to their timestamp and only the 10 most recent tweets are being fetched. That said, Firebase still returns the records in ascending order that’s why you had to call reverse() on the TweetList component earlier to reverse the ordering.

resolveFirebaseQuery(sub) { return this.fb.child(tweets_subkey).orderByChild('timestamp').limitToLast(10); }

Add the Styles

Open the public/index.html file and add the following inside the <head> :

This uses picnic.css to add some default styling to make the project look good.

Next, create a src/index.css file and add the following:

body { margin: 0; padding: 0; font-family: sans-serif; } .tweets { margin-top: 30px; } .tweet { padding: 20px; }

Conclusion

That’s it! In this tutorial you’ve learned how to use MobX to act as a data store for your Firebase data. As you have seen, using Firebase with MobX doesn’t completely change the way you interact with Firebase so you can still use your existing knowledge of the Firebase API. You can check out the code used in this tutorial in this Github repo.