Contact Me Part II — The Client

Contact Me Part II — The Client

Build a frontend app with React, Apollo and GraphQL

Nov 26, 20179 min read

Overview:

This is the second part of this tutorial, which makes use of the GraphQL server we made in the first part. If you just landed here, check out the first part on how to build the GraphQL server for the Contact Me app. In this part of the tutorial, I will help you build a user interface using React, where a user can type their email address and the message they want to send us. We will use the Apollo client to send a mutation to our GraphQL server which processes the message and sends us an email containing the email address of the sender and the message they sent. Finally, I will help you deploy your app using Surge.

Setup:

We are going to use the create-react-app cli tool to setup our frontend app. If you don’t have the cli tool yet, you can execute the following command to install it globally in your system using npm:

npm install -g create-react-app

Then, let’s go to our projects folder:

cd messageMe

Here, we will use the create-react-app command to create a new app and call it client.

create-react-app client

After the process is complete, you should have a new folder named client with the basics of a React app all setup and ready to run. Next, let’s go into the new folder and start the development server:

cd client yarn start

This should open a new page in your browser at localhost:3000 with the default React welcome page.

App.js

First thing we need to do is create a new folder named components in the src folder. This is where we will create and keep all our view components. Inside the folder, let’s create a new file named Main.jsx which is going to be our parent component in our component hierarchy.

import React, { Component } from 'react'; class Main extends Component { render() { return ( <div> Hello World! </div> ); } } export default Main;

Next, let’s remove all the default code from your App.js and render the Main component.

import React, { Component } from 'react'; import Main from './components/Main'; const App = () => <Main />; export default App;

Material-UI

Material-UI is a set of React components that implement Google’s material design. I will help you build the form using material-ui components. First thing we need to do is install it in our app. Additionally, let’s install bootstrap, primarily for structuring our content.

yarn add material-ui bootstrap

Next, let’s wrap the Main component with the MuiThemeProvider module that we can import from the material-ui package. Also, don’t forget to import bootstrap from the node modules that we just installed.

import React, { Component } from 'react'; import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'; import '../node_modules/bootstrap/dist/css/bootstrap.min.css'; import Main from './components/Main'; const App = () => ( <MuiThemeProvider> <Main /> </MuiThemeProvider> ); export default App;

Doing this let’s us import UI components in any of our components from the material-ui module.

Main.jsx:

Next, let’s create UI components for our app. You will need three basic components to build the contact form.

  1. Email Form
  2. Message Form
  3. Send Button

Let’s build each component in a separate file for better modularization.

EmailForm Component:

Let’s create a new file named EmailForm.jsx in the src folder.

import React from 'react'; const EmailForm = ({}) => { return ( <div> EmailForm </div> ); } export default EmailForm;

Next, let’s import this component in our Main component and render it. We will later need to perform API requests to our GraphQL server form the Main component, which means that we need to store our input data in the Main component. We can use the component’s state to store the input data, starting with the user’s email. One last thing, I am going to add the text-center class from bootstrap which will align everything to the center, giving us a neater UI.

import React, { Component } from 'react'; import EmailForm from './EmailForm'; class Main extends Component { state = { email: '' } render() { return ( <div className="text-center"> <EmailForm email={this.state.email} onEmailChange={this.onEmailChange} /> </div> ); } } export default Main;

Next, let’s edit the EmailForm component. We are passing two props to it, which we need to receive in the EmailForm component. Here, let’s add the TextField UI component from material-ui. For the TextField, we will use the following props:

  • value: Which is going to be the email value that we receive as props.
  • floatingLabel: Which is going to be our placeholder value to prompt the user.
  • onChange: Which needs a function name to handle changes in the TextField.

Let’s work backwards to write the onChange function by first receiving it as props.

import React from 'react'; import TextField from 'material-ui/TextField'; const EmailForm = ({ email, onEmailChange }) => { return ( <TextField value={email} floatingLabelText="Enter your email:" onChange={onEmailChange} /> ); } export default EmailForm;

Next, let’s create the onEmailChange function in our Main component. The function, by default, will receive the event and the newValue as its arguments. We can use the newValue to update the state value for email.

import React, { Component } from 'react'; import EmailForm from './EmailForm'; class Main extends Component { state = { email: '' } onEmailChange = (event, newValue) => this.setState({ email: newValue }); render() { return ( <div className="text-center"> <EmailForm email={this.state.email} onEmailChange={this.onEmailChange} /> </div> ); } } export default Main;

Now, when you type your email in the text field, the state value updates accordingly. We are primarily focusing on creating the basics with this tutorial, meaning there are a lot of additional security features you can implement to validate the email.

MessageForm Component:

Now that we have the email value, we need to add the message value. The process will be similar with small changes in the TextField. First, let’s create a new file named MessageForm.jsx. Here, let’s add additional props in the TextField to support multiLine. I added the floatingLabelStyle to prevent the display style to change after adding multiLine.

import React from 'react'; import TextField from 'material-ui/TextField'; const MessageForm = ({ message, onMessageChange }) => { return ( <div> <TextField value={message} multiLine={true} floatingLabelText="Say hi!" floatingLabelStyle={{ left: 0 }} onChange={onMessageChange} /> </div> ); } export default MessageForm;

Next, let’s work backwards and plug the MessageForm component to our Main component. First, let’s import the component in the Main component to render it. Then, let’s add a state variable that will store the message value. Finally, let’s write the onMessageChange handler to update the state when the message is being typed.

import React, { Component } from 'react'; import EmailForm from './EmailForm'; import MessageForm from './MessageForm'; class Main extends Component { state = { email: '', message: '' } onEmailChange = (event, newValue) => this.setState({ email: newValue }); onMessageChange = (event, newValue) => this.setState({ message: newValue }); render() { return ( <div className="text-center"> <EmailForm email={this.state.email} onEmailChange={this.onEmailChange} /> <MessageForm message={this.state.message} onMessageChange={this.onMessageChange} /> </div> ); } } export default Main;

Now that we have all the data that we need, let’s add the SendButton component.

SendButton Component:

The SendButton component should display a button and call the sendMessage function, which should be defined in the Main component. Let’s use the FlatButton component from the material-ui module to create the button.

import React from 'react'; import FlatButton from 'material-ui/FlatButton'; const SendButton = ({ sendMessage }) => { return ( <FlatButton onClick={sendMessage}> Send Message </FlatButton> ); } export default SendButton;

Next, let’s plug in the SendButton component to our Main component and write the sendMessage function. For now, it doesn’t have to do anything.

import React, { Component } from 'react'; import EmailForm from './EmailForm'; import MessageForm from './MessageForm'; import SendButton from './SendButton'; class Main extends Component { state = { email: '', message: '' } onEmailChange = (event, newValue) => this.setState({ email: newValue }); onMessageChange = (event, newValue) => this.setState({ message: newValue }); sendMessage = () => { console.log('sendMessage'); } render() { return ( <div className="text-center"> <EmailForm email={this.state.email} onEmailChange={this.onEmailChange} /> <MessageForm message={this.state.message} onMessageChange={this.onMessageChange} /> <SendButton sendMessage={this.sendMessage} /> </div> ); } } export default Main;

Now that all our views are in place and fully functional the next thing we need to do is connect our app with the GraphQL server we built and send a mutation request to the server.

Apollo:

Since we are using React, there are specific packages that are required to run Apollo and React together. Most of the related information can be found here. First, let’s install all the packages we need with the following command.

yarn add apollo-client-preset react-apollo graphql-tag graphql

Let’s look at each package individually to see why we need them:

  • apollo-client-preset: Contains the ApolloClient component and all necessary modules to instantiate an Apollo client to connect it to our GraphQL server.
  • react-apollo: Contains the ApolloProvider component which helps us use Apollo anywhere in our React component hierarchy.
  • graphql-tag: Parses all our GraphQL queries and mutations so that our GraphQL server can understand it.
  • graphql: All the modules above are dependent on this core module, which makes using GraphQL possible in our React components.

Now that we have all the necessary packages for Apollo, let’s make use of the ApolloClient and the ApolloProvider.

ApolloClient:

In our App.js, let’s create a new client instance by adding the following code. You should use the HttpLink instance to add your deployed GraphQL server running on Heroku.

import React, { Component } from 'react'; import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'; import '../node_modules/bootstrap/dist/css/bootstrap.min.css'; import Main from './components/Main'; import { ApolloClient } from 'apollo-client'; import { HttpLink } from 'apollo-link-http'; import { InMemoryCache } from 'apollo-cache-inmemory'; const client = new ApolloClient({ link: new HttpLink({ uri: 'https://YOUR_GRAPHQL_SERVER.herokuapp.com/graphql' }), cache: new InMemoryCache() }); const App = () => ( <MuiThemeProvider> <Main /> </MuiThemeProvider> ); export default App;

ApolloProvider:

Next, let’s import the ApolloProvider component from the react-apollo package and wrap our Main component with it. The ApolloProvider component requires the client we just created as it’s props.

import React, { Component } from 'react'; import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'; import '../node_modules/bootstrap/dist/css/bootstrap.min.css'; import Main from './components/Main'; import { ApolloProvider } from 'react-apollo'; import { ApolloClient } from 'apollo-client'; import { HttpLink } from 'apollo-link-http'; import { InMemoryCache } from 'apollo-cache-inmemory'; const client = new ApolloClient({ link: new HttpLink({ uri: 'https://YOUR_GRAPHQL_SERVER.herokuapp.com/graphql' }), cache: new InMemoryCache() }); const App = () => ( <ApolloProvider client={client}> <MuiThemeProvider> <Main /> </MuiThemeProvider> </ApolloProvider> ); export default App;

We have now hooked Apollo to our React component hierarchy. Next, let’s send mutations from our components. So exciting!!!

Sending Mutations:

To send mutations, let’s add two more packages in our Main component.

import { graphql } from 'react-apollo'; import gql from 'graphql-tag';

We will use the graphql module to wrap our Main component which will connect React and Apollo. The GraphQL method requires GraphQL queries as its arguments. The queries should be wrapped with the gql module from the graphql-tag package.

const sendMessage = gql` mutation($email: String!, $message: String!) { createMessage( email: $email, message: $message ) { email message } } `; export default graphql(sendMessage)(Main);

Notice that the mutation inside our gql tag is the same as the one we experimented with using the GraphQL Playground in the first part of the tutorial. Here, we are also adding variables in our mutations. Wrapping our component with graphql gives us access to GraphQL functions through props. Therefore, we can call this.props.mutate() , which we can also use to pass in the query variables. Next, let’s update the sendMessage method inside our component class to send the mutation when we click the SendButton.

sendMessage = () => { const { email, message } = this.state; this.props.mutate({ variables: { email, message } }) .then(data => { this.setState({ email: '', message: '' }); }) .catch(error => console.log('error:', error)); }

After your server finishes sending the email, we should get back the data that we send from our server. Right now, our server doesn’t return anything useful, but we can take this opportunity to update the then() method to reset our state variables. This will clear the form after the user sends a message. If everything works well, when you press the SendButton, you should get your email. How cool is that?

Surge:

Finally, we are going to build this app for production. If you don’t have surge installed on your system, you can get it from npm using the following command:

npm install -g surge

Next, let’s run the build command to build the production version of our app:

yarn build

Then, let’s go into the build folder and run the following command:

surge

You will have to sign up and/or sign in if you haven’t yet. You can choose a domain name if you want to. I am going to go with the incongruous default domain name surge gives me. After the process completes, open the link in your browser and test it. Awesome right? Now you can share what you just built.

In a nutshell:

We now have a fully functional app that let’s our users send us messages directly to our inboxes. Checkout my version here and please let me know how I could have done it better. This app is complete, but you can still add a lot more features including email verification, error feedback for users or even profanity filter for your messages. Don’t hesitate to email me your deployed version for feedbacks or if you have any questions using the app above ;).

Happy Coding!