In order to achieve this, we're going to use a very different approach compared to the previous lessons. Our Phoenix app will deliver all the initial static content on the first page load, and then act only as a thin API client to transmit JSON data.
Moving our JS file
This file gets imported as soon as we fire up any Phoenix page, since it's imported in in our layout template (
/messages path. Therefore, let's delete the line that imports it in our layout template, and instead add it to our newly created Chat template instead:
Setting up Babel and React
brunch-config.js file. Scroll all the way down until you see the
brunch-config.js and look for the
plugins key, and make sure that you add
"react" in the list of presets:
That's it! Now let's try out these new fancy libraries!
"Hello world" in React
To make sure that React and Babel work, we'll render a simple "Hello world" text on our
/messages page. Start by opening up
assets/js/app.js and delete everything that's in it (including the
import "phoenix_html" statement), and replace it with this code instead:
Let's quickly go through what we're doing here, in case you aren't familiar with React:
We import our two newly installed dependencies – React and ReactDOM, so that we can use them in the file
We create a new
Appclass, which is a React component. In it, we simply specify that it should render a
div-tag containing the string "Hello world!".
Finally, we use ReactDOM to render this component. We specify the name of the component (
App), and the element on the page that it should be rendered inside of (an element with the id
This element with the ID "app" doesn't exist on our messages page yet, so we need to create it:
After doing this, you should see "Hello world!" when you reload
/messages (if not, you might have to restart your Phoenix server).
Composing our UI
Now, you might be thinking "Gee, that was a lot of work just to render a shitty 'Hello world!' text"! And yes, you'd be right about that. But remember what the interface that we actually want to build looks like:
With that out of the way, let's start building our messaging interface! Whenever you use a component-based UI library such as React, it's good to try to estimate in advance the number of components that we're going to need.
Based on our Sketch file, it seems logical to split our app into four components:
We're going to create a new folder in our
js folder called
components where we'll put these.
We'll start by creating our two main components –
ChatContainer. Since this isn't really a React course, we won't go through every little part in detail, so you can just copy and paste the following code. You'll notice that our React components are basically just outputting HTML anyway, the only caveat with JSX is that if you want to add a
class to an element, you have to use the
className attribute instead.
As you can see, we're importing the
MenuMessage component in our menu container, and the
ChatMessage component in our chat container and rendering them inside a list. So we need to create these components as well!
Finally, now that all our components have been defined, we simply need to import
MenuContainer into our initial
app.js file, and render them there instead of "Hello world!":
Now restart your Phoenix server and reload the page. If everything went well, you should see all the HTML from your components rendered onto the page!
To make it look good, we can create a new
messages.scss file in our
css folder, and add these styles (sorry, it's long, but it's the last CSS we'll need!)
Adding fake data
Right now, our React templates contain static data such as "John Doe" and "Here's a message!". We want to remove this static data from our templates and instead use whatever data is given to the components via props!
There are primarily three types of data in our UI that we need to account for – rooms, messages, and users. The "rooms" list is the collection of conversations in our
MenuContainer. Each room then contains a list of "messages", which are rendered in the
For now, we'll just define some fake data in a separate file (don't worry, in the next chapter, we'll learn how to fetch it from the database instead). Let's create a file called
fake-data.js in our
js folder and add a big JS object inside of it that contains all the data we're interested in:
Let's take a moment to analyse this data. We have a single room with an ID of . Our counterpart (the other user) in that room is "alice", who has the user ID . Alice and our user have a list of messages. The first one ("Hi!") is
outgoing, whereas the second one ("Hello there") was sent from her to us.
Alright, let's render this data! We'll start by importing the fake data into
app.js, and there we'll also create the constants
MESSAGES. The first constant is passed as a prop to the
MenuContainer and the second one is passed as a prop to the
Next, we go to the
MenuContainer. The important thing here is that we want to render each room as a
MenuMessage inside our
ul tag. In other words, each object in our
ROOMS-data needs to be transformed into a
map function. Notice that we pass down every
room as a prop to the
MenuMessage component too.
Then, we simply render our newly created
rooms variable inside the
Next, we go to
MenuMessage. Now that we receive the
room prop from
MenuContainer, we can correctly render that data in the component.
There are three things that we want to render here (if we exclude the avatar, since we don't have any fake data for that yet) – the username of the counterpart, the last message that was sent (by either of us), and the time when it was sent. All this can easy be extracted from the
room prop that we receive:
After doing this, you should be able to see our fake data in the Messages menu!
Next, we move on to the
ChatContainer. Here, we apply the same principle as with the
MenuContainer – we want to render each item in the
messages list as a
ChatMessage component. Again, we use the
.map function, and we replace the content inside the
ul-tag with our new
messages variable instead:
Finally, we go to
ChatMessage. Here, we first need to know if the message is outgoing or not. If it is, we apply the class
"user" on our
li element, otherwise we use
"counterpart" instead. This will render the chat bubbles as either blue (outgoing) or gray (incoming). Then we simply render the text.
And now, we're finally done!
In the next chapter, we're going to go back to Elixir (yay!) to learn how to fetch this data from our database and render it as JSON so that we can use it in our client.