To get started with the app, clone the repo and then install the needed gems:
$ bundle install --without production
Next, migrate the database:
$ rails db:migrate
(Optional) Seed the database:
$ rails db:seed
Run the dev server:
$ rails server
This app was created to demonstrate an approach to combining [React] (https://facebook.github.io/react/) & [Rails] (http://rubyonrails.org/). The focus of the app is on React, and so [Devise] (https://github.com/plataformatec/devise) was brought in to handle users. This app can be thought of as a re-creation of Michael Hartl's [Ruby on Rails Tutorial] (https://www.railstutorial.org/book/) using React and Devise.
Instead of adding react components into rails views via the react helper
(which looks like <%= react_component('HelloMessage', name: 'John') %>
), the server
renders react components and props from the controller. This means that the only rails
views rendered are the base application.html.erb view and Devise's views.
Unlike in Hartl's tutorial, this app interacts with a micropost resource (a JSON API). As an example, when a user
visits their profile, the Profile
react component is mounted, which triggers its
componentDidMount()
function, which sends out an ajax request for a user's microposts. The server
then sends those microposts back to be rendered by the Profile
component (which it does via
the ShowMicroposts
child component). Similarly, the UserIndex
component makes use of a users resource.
There are a total of 11 components found in app/assets/javascripts/components
. Two of them are
parent components (Profile
and UserIndex
), one of them is a stand-alone component (Home
), and
the rest are children that help the two parent components.
When you first visit the site, you are greeted by the Home
component. This component is a simple
Javascript function. Clicking on "Sign up" takes you to the user
creation form served up by Devise. After successfully creating an account, we arrive back at
the home page, but it looks different this time. Because we are signed in, the home page is
now rendered by the Profile
Class component. This component uses state and props to put
itself together.
On the left we see our username and some other information, all of it brought to us by the
UserInfo
child component. This component uses the UserGravatar
component to render a
[gravatar] (https://www.gravatar.com), and the UserStats
component to show our followers / following info.
Below all of that we see the micropost form, which is yet another react component (called MicropostForm
).
Writing a post and clicking the "Post" button has our post show up in our feed without a page refresh.
When the "Post" button is clicked, it is handled by an onSubmit
function within the MicropostForm
component,
which sends the value of the form to the server via ajax. On successful submission the micropost count is incremented
and the micropost feed is refreshed asynchronously. On the right of the page is our micropost feed, rendered
by the ShowMicroposts
component. If we now click the 'delete' link on our new post, the post
will be deleted asynchronously.
Clicking the "Users" link in the header takes us to a list of all the users on the site. This page is
rendered by the UserIndex
component. At the bottom of the page we see a simple pagination system
brought to us by the HandlePagination
component. This component works with the rails backend to
provide a very simple pagination mechanism (very simple). It works asynchronously to load up
the next or previous page of users.
Clicking on a user's username takes us to their profile page, which is rendered by the Profile
component.
Under the user's stats we see a follow button, and clicking it sends out an ajax request that lets us
follow the user and increments the user's 'followers' number. The process of following a user requires
that the FollowButton
component communicate with the UserInfo
component to let it know to increment
the number of users it has in its state. It works like this:
- a user clicks the follow button
- an ajax request is sent out from the
FollowButton
component - on success, an
updateFollowers
function is triggered in the parentProfile
component updateFollowers
increments thefollowers_count
state in theProfile
component- The new state of
followers_count
is sent down to theUserInfo
component - The
UserInfo
component'scomponentWillReceiveProps
function fires and updates thefollowers_count
in it's state - React renders the new state of
UserInfo
This process of [lifting state up] (https://facebook.github.io/react/docs/lifting-state-up.html) is how components communicate with eachother, because in React [data flows down] (https://facebook.github.io/react/docs/state-and-lifecycle.html#the-data-flows-down).
If we now navigate back home, we see that the other user's microposts are included in our
feed and our 'following' number has been incremented. We can click on the 'following'
link in our stats section to visit the 'Following' page, which is rendered by the
UserIndex
component.
- Pressing the back button was causing some components to misbehave, so I had to disable turbolinks caching in the header of the application.html.erb view.
- Since the app uses react components, instance variables are unavailable. Everything the component needs must be sent out by the controller (which is packaged in an @props instance variable to aid in testing) or received via an ajax call (which is how, for example, the micropost feed is obtained).
- I've added a unique username field to the user model, and there are no email activations.
- There's no image upload for the microposts (shouldn't be difficult to add, but since my focus was on building the React UI, I skipped over it)
Tests are implemented in two ways: React tests are handled with [jasmine] (https://jasmine.github.io/),
and are found in spec/javascripts/components
, Rails tests are handled
by [Rspec] (https://github.com/rspec/rspec-rails).
To run the rspec test suite:
$ rspec
To run the jasmine test suite:
$ rails jasmine
- More tests
- Improve the 'created at' time in a user's micropost