The 'home page which displays a table of all banners
A form to add a new banner
A view that displays a banner and provides the functionality to toggle the display status of the banner
Intro to Reflux
RefluxJS is one of the many Flux implementations and I like its simplicity and use of mixins to add functionality. In general, I found Flux difficult to grok. I read the FB docs, waded through various blog posts (Flux For Stupid People and Getting to Know Flux) and even watched all of the egghead.io React and Flux videos but I honestly didnt like it. Luckily I stumbled across React.js architecture - Flux VS Reflux and it saved the day. I thought the author was reading my mind and was somehow part of the NSA. The first paragraph blew me away. While Flux seemed architecturally complex and bloated with boilerplate code, Reflux seemed streamlined and succinct. It drastically simplified the process of listening to changes in stores, made working with actions easier and less verbose and got rid of the Dispatcher entirely!! Stop now and check out the blog post for a complete comparison.
To run the application locally, simply clone this repo
Then install all of the dependencies for the project defined in package.json:
Now you can fire the app up with the following command and then open http://localhost:9000 to run the app:
Show the Code!
Lets take a look at the important files in the application. I wont cover everything but the great thing about React and Reflux is that its somewhat simple and easy to follow.
Since this is an SPA, index.html is the only page that well need. Its simply bootstrap markup with navigation, a title and a div (id=app) where React will render our application. Thats it.
The app.js file sets up our application, defines the routes and renders the app. For our application well be using react-router which provides wait for it wait for it routing for our application. The routes variable defines the view hierarchy for our application. We then declare a view hierarchy with nested s and provide them with a React element to handle the route when its active.
The React component simply renders the component that, in turn, renders the currently active child route for the application. The last section, Router.run, is somewhat magical. When the user selects a route, the run callback receives Handler, that has all of its appropriate information wrapped up in it. If our app would be managing some type of state, we could pass this state down the view hierarchy. For our use case, we simply use the standard boilerplate and render our application into the app div.
The heart of the application is the Reflux store which holds all of the model and business logic. The store holds private array of banners, _banners, which the users interact with. In our init function, this array is populated by the defaultBanners function. Were using a simple array of banner objects but this is where youd typically fetch your data from some API. We also register some actions (toggleStatus and addBanner) that the store listens for and binds them to individual function that handle the appropriate logic.
We also have a couple of getter methods that returns the array of private banners and a specific banner by its ID.
The addBanner function is called whenever the store hears an addBanner action. It pushes the new banner received to the private array of banners and updates the DOM to display the new row in the home page table.
The toggle function is called whenever the store hears a toggleStatus action. This function find the appropriate banner in the private array by its ID and then toggles its active property. The trigger method passes the change notification to any listeners to update the DOM.
The action.js file uses Reflux is much smaller and simpler than the standard Flux Dispatcher. It simply defines the actions that our app will broadcast. Not much to see here.
This view component is responsible for displaying our table of banner data. When initialized, the getInitialState method is called and loads the banner data from the store into the state. When the component renders, it first creates a variable of rows that is used to display the actual data in the table rows.
The view.js file is pretty interesting and has a lot going on. First, the class uses an array of mixins to add functionality to the component. When the component initially mounts, is uses Refluxs ListenerMixin to listen for changes in the BannerStore and act accordingly. The getInitialState method grabs the ID of the banner being viewed and calls the BannerStores getBanner method and adds it to the state. When the component renders, it displays this state data on the page.
There is also a 'Toggle Active button that, when clicked, broadcasts actions.toggleStatus and passes the ID of the banner. The BannerStore is responsible for toggling the Yes/No status of the banner and then notifies any listeners that there has been a change. This view component listens for any change to the BannerStore and then calls toggleStatus to change the state and update the DOM.
Our final component displays a form for entering a new banner. IMHO I takes more work that I think it should. I tried to implement a couple of libraries such as react-froms and react-bootstrap but had much better luck rolling my own for this simple application.
When the component initializes, getInitialState sets up the state with default error and banner objects. The errors object be will used to notify the user that a field is required when submitting while the banner object will default in some data to the form fields.
When the component renders it calls renderTextInput for each of the three form fields. This adds the appropriate HTML to the DOM to make the field look pretty and sets up any error notifications when the form is submitted. The value of the form field is bound to the banner in the state and fires the handleChange event whenever the user changes the text (i.e., typing). The handleChange function updates the state which re-renders the DOM node for the form field.
When the user clicks the submit button, the forms onSubmit handler calls the handleSubmit function which check to make sure all fields are filled out. If a required field is blank, it adds this to the states error object which display the field in a red box. If everything is filled out correctly, it calls the BannerStores addBanner method with the new banner data, resets the components state and display the home page which show the newly added banner in the table.
So there you have it, a minimal React and Reflux application that you can use as a starter for your own project. Next week well start building a Node API for our application.