Creating Responsive Web Apps Using ReactJS, NodeJS and ReactiveX Part 3

in #utopian-io7 years ago (edited)

Repository

https://github.com/facebook/react

Welcome to the Tutorial of Creating Responsive Web Apps Using ReactJS, NodeJS and ReactiveX Part 3

What Will I Learn?

  • You will learn how to update server part to store sketches.
  • You will learn how the serving and piping of sketches done through client.
  • You will learn how to setting up the socket logic from client.
  • You will learn how to manage the client code to save sketches.
  • You will learn how to list all sketches using client in our application.
  • You will learn about the testing of real time results using local host.

Requirements

System Requirements:
OS Support:
  • Windows 7/8/10
  • macOS
  • Linux

Difficulty

  • Intermediate

Resources

Required Understanding

  • You need a good knowledge of HTML, CSS and JavaScript
  • A fair understanding of Node.js and React.js
  • A thirst for learning and developing something new

Tutorial Contents

Description

Since you know how to install and run RethinkDB, as well as the way to execute and subscribe to changes in Rethinkdb Database. You have also studied how to develop database schema and test the results in the Second Part of the tutorial, if you don't check the second tutorial check the second part, Now you will learn how to update the server side code to store as well to serve the list of sketches. We will also see what will be the possible changes in the client side code so that it would be able to save new sketches and also to list the set of sketches.

Updating Server to Store Sketches:

Now you're going to use the same basic pattern that you used for the timer to maintain a list of sketchings in the DB. You'll code up some functionality to store a new sketching to a table, as well as some logic to pipe existing and new sketchings over interested sockets. First thing you need to get out of the way is to create a new table that will store the sketchings. Go into the RethinkDB console and add a new table to the awesome_whiteboard database called sketchings.


You're going to follow along with the basic same pattern that you did for the timer, except you're also going to take a sketching that comes in from the socket and store it to the database.

function createSketching({ connection, name })



In the server index file, create a new function called createSketching, and make it take a connection and a name you can use for the sketching.

r.table('sketchings')


Now call r.table, and specify sketchings for that. Chain on an insert statement, and add a new object with a name property and a timestamp that you set to the current date.

.insert({
    name,
    timestamp: new Date(),
  })

Chain on a run statement and pass it the connection. This returns a promise, so chain a then on here and just write out a success message to the console. The success message doesn't matter as long as it makes sense.

  .run(connection)
  .then(() => console.log('created a new sketching with name ', name));

This is going to come from the client, so in our code at the bottom of this file, get rid of the timer stuff you added earlier.


Subscribe to an event coming from the socket called createSketching. Pass that a handler function. Make this take an object that has a name property. In here, just call the createSketching function, passing on the connection that you have available here and the name for the sketching.

  client.on('createSketching', ({ name }) => {
      createSketching({ connection, name });
    });

So now it's possible for the client to emit this event and should end up with a sketching being saved to the RethinkDB

Serving Sketches Using Rethinkdb:

Below where you have the createSketching function, add a new function called subscribeToSketchings and make it take a client, which is the client side of the socket and the connection, which is the RethinkDB connection.

function subscribeToSketchings({ client, connection }) {

}

Open a query to the sketchings table and specify that you're interested in changes. This time, pass an object to this changes method call and specify that you want include_initial to be true.

r.table('sketchings')
  .changes({ include_initial: true })

This option just tells RethinkDB that when you execute the query, you want it to call back to us for each row that is already present that matches your query. Now run the query, passing it the connection, and this returns a promise, so add a then on it, providing a callback function that gets the resulting cursor.

.then((cursor) => {
  });

On the cursor, call each and provide it with a callback function that takes an error and a sketchingRow, the row containing the new sketching.

cursor.each((err, sketchingRow) => client.emit('sketching', sketchingRow.new_val));


In this function, you emit to the client with an event name of sketching and pass the new_val object that you got from the row as the event payload.

Our Socket Logic:

So very much the same as you did with the timer, just with a bit more structure, and you specified that you wanted to get the initial values on the cursor. Now in our socket logic, you subscribe to an event from the client by calling on and specifying an event of subscribeToSketchings. Give it a function to execute when the client emits this event, and call subscribeToSketchings

client.on('subscribeToSketchings', () => subscribeToSketchings({ }));


Now passing it the client and the connection. So when the client emits this event,

client.on('subscribeToSketchings', () => subscribeToSketchings({
      client,
      connection,
    }));

your code will pipe the existing sketchings through to it, as well as any new sketchings that make it into the table.

Changing Client Code to Save New Sketch:

Let's go use this functionality now on the client side. Open up the api.js file in the src folder in the client folder. Change this function name to be subscribeToSketchings, and subscribe to sketching instead of the timer event, and pass the sketching onto the callback.

unction subscribeToSketchings(cb) {
  socket.on('sketching', sketching => cb(sketching));
  socket.emit('subscribeToSketchings');
}

Also, emit subscribeToSketchings instead of what you have at the moment. Now add a new function called createSketching.

function createSketching(name) {
  socket.emit('createSketching', { name });
}

It should have one parameter called name’. Here, justemitan event calledcreateSketching, which you wired up earlier on the server side, and pass it an object containing thename` for the sketching.

You can go call these functions from your React components. You’ll need to go and create those components first. In the src folder in your client folder, create a new file SketchingForm.js. This will contain the form component for creating a new sketching.

import React, { Component } from 'react';


In this file, import React and Component from the React npm package. You'll also want to import the createSketching function from the `api file’.

import {createSketching} from './api';


Create a new React component called SketchingForm that extends from Component. Add some initial state with a sketchingName value that equals an empty string.

class SketchingForm extends Component {
  state = {
    sketchingName: '',
  }

Add the render method, return a div with a class name of form. The starter code contains all the starting in the App.css file. Inside of this div, add a form.

Inside the form, add an input component, which I'll use to take the name of the sketching. It's of type text. Its value should reflect the sketchingName on state, and you should add an onChange value. And in here, just state the sketchingName value on state to the event's target value. Add a placeholder of Sketching name, a className of Form-sketchingInput, and make the field required.

render() {
    return (
      <div className="Form">
        <form onSubmit={this.handleSubmit}>
          <input
            type="text"
            value={this.state.sketchingName}
            onChange={(evt) => this.setState({ sketchingName: evt.target.value })}
            placeholder="Sketching name"
            className="Form-sketchingInput"
            required
  />
    );
  }

Put a button below the input with type of submit and className of form button. The button text should be Create. Add a class method called handleSubmit onto the class that takes an event. Inside, call the preventDefault method on the event to ensure that it doesn't bubble up. Now call the createSketching function that you imported from api, and pass on the sketchingName from state.

handleSubmit = (event) => {
    event.preventDefault();
    createSketching(this.state.sketchingName);
  }

Afterwards, just set the sketchingName to an empty string on state. And now that we've got the handleSubmit function defined,

this.setState({
      sketchingName: '',
    });

We can use it on the form. Now open the App.js file. In here, first clean it up by getting rid of the constructor. And after you've done that, also get rid of the default state. Clean out the message in render too. Now import the SketchingForm component at the top of this file.

import SketchingForm from './SketchingForm';


Just go and add the SketchingForm in the render method below where you have your header code, this out in the browser. You'll see that you should be able to specify a Sketching name and click the button, and the text box gets cleared out.

But did anything happen?

Go take a look. Open up your Rethink console, go to the Data Explorer, and run the following query:

r.db(‘awesome_whiteboard’).table(‘sketchings’)


You should see your sketching being returned.

Listing of Sketches Using Client:

So you have the ability to add a new row to the sketchings table. Next, you'll create a component that shows the items in a list by subscribing to the values over the WebSocket. In the src folder in your client code, add a new file and call it SketchingList. You can copy the work you did from the SketchingForm component to get the basic React component structure in place. Just copy everything from the SketchingForm file, and paste it into the SketchingList file. Change the class name from SketchingForm to SketchingList, and also remember to export at the end of the file using the new name.

export default SketchingList;


Clear out the render function for now. You'll code up the logic there soon enough. Under the false state, remove the SketchingName state prop, and state a prop called sketchings to an empty array.

At the top of the file, import the subscribeToSketchings function from the api module instead of the createSketching function.

import {subscribeToSketchings,} from './api';


Now add a constructor to your SketchingList component, and remember to make it take a props parameter. Call super, passing on the props.

constructor(props) {
    super(props);
  }

Now call the subscribeToSketchings function, which, as you might recall, expects a callback that it can call for every sketching that will come back on the socket. In this callback function, just call setState using the function syntax, which takes a function that will be called with the previous state.

subscribeToSketchings((sketching) => {
      this.setState(prevState => ({
        sketchings: prevState.sketchings.concat([sketching]),
      }));
    });

From this, just return the state with a new sketching added on to the end of the array. So when our component gets constructed, it will subscribe to sketchings from the server. In render function, you can see that we also need to remove the handleSubmit function over here. We don't need it anymore so remove it.

const sketchings = this.state.sketchings.map(sketching => ());


Moving onto the render function, define a new const called sketchings and set that to the result of mapping over the sketchings array on state. Map takes a function that should return the desired result for each item in the array, so add that.

  <li
        className="SketchingList-item"
        key={sketching.id}
      >

From this return an li with a className of SketchingList-item, and you also need to specify you need key because you'll have multiple of these items in the list. Set that to the sketching.id. For the inner text of the li, just use the sketching.name.

  return (
      <ul
        className="SketchingList"
      >
        {sketchings}
      </ul>
    );

Below the map code, you return a ul, and you use the sketchings you just calculated as its children. Specify a class name of SketchingList for the ul. So now you have a component that subscribes to sketchings from the server over the socket when it gets constructed and then adds them to an array on state as it gets in. This component renders all the items in the array as a list item in an unordered list, pretty simple.

Checking the Results:

Go plug in your SketchingList component by heading over to the App.js file and duplicating this SketchingForm input.

import SketchingList from './SketchingList';



Rename it to import the SketchingList component instead. Great! So now if you open up the browser, you should see the sketchings, you've added previously using the form being displayed in there.


Open up another browser instance and keep it side by side with the one you've got open currently. Navigate to localhost:3000 so that you've got the app open in both browser windows. In the one, add a new sketching. You should see the other one updating in real time, and that was the whole point.


Summary:

In this tutorial, we learned how to update server to make it happen to store the sketches as well as serve them. We have also studied about the management of client side code to save a new sketch and display all the list of sketches in our application. In the end, we have seen our results by connecting our application to local host server. In the next tutorial we will have look at developing components of our sketching application. Including subscribing using react and improve the rendering time of sketching component.

Source Code:

//index.js//

const r = require('rethinkdb');
const io = require('socket.io')();

function createSketching({ connection, name }) {
  return r.table('sketchings')
  .insert({
    name,
    timestamp: new Date(),
  })
  .run(connection)
  .then(() => console.log('created a new sketching with name ', name));
}

function subscribeToSketchings({ client, connection }) {
  r.table('sketchings')
  .changes({ include_initial: true })
  .run(connection)
  .then((cursor) => {
    cursor.each((err, sketchingRow) => client.emit('sketching', sketchingRow.new_val));
  });
}


r.connect({
  host: 'localhost',
  port: 28015,
  db: 'awesome_whiteboard'
}).then((connection) => {
  io.on('connection', (client) => {
    client.on('createSketching', ({ name }) => {
      createSketching({ connection, name });
    });

    client.on('subscribeToSketchings', () => subscribeToSketchings({
      client,
      connection,
    }));
  });
});


const port = 8000;
io.listen(port);
console.log('listening on port ', port);
//App.js//

import React, { Component } from 'react';
import './App.css';
import SketchingForm from './SketchingForm';
import SketchingList from './SketchingList';

class App extends Component {
  render() {
    return (
      <div className="App">
        <div className="App-header">
          <h2>Our awesome sketching app</h2>
        </div>

        <SketchingForm />

        <SketchingList />
      </div>
    );
  }
}

export default App;
//api.js//

import openSocket from 'socket.io-client';
const socket = openSocket('http://localhost:8000');

function subscribeToSketchings(cb) {
  socket.on('sketching', sketching => cb(sketching));
  socket.emit('subscribeToSketchings');
}

function createSketching(name) {
  socket.emit('createSketching', { name });
}

export {
  createSketching,
  subscribeToSketchings,
};
//SketchingForm.js//
import React, { Component } from 'react';
import {
  createSketching,
} from './api';

class SketchingForm extends Component {
  state = {
    sketchingName: '',
  }

  handleSubmit = (event) => {
    event.preventDefault();
    createSketching(this.state.sketchingName);
    this.setState({
      sketchingName: '',
    });
  }

  render() {
    return (
      <div className="Form">
        <form onSubmit={this.handleSubmit}>
          <input
            type="text"
            value={this.state.sketchingName}
            onChange={(evt) => this.setState({ sketchingName: evt.target.value })}
            placeholder="Sketching name"
            className="Form-sketchingInput"
            required
          />
          <button
            type="submit"
            className="Form-button"
          >Create</button>
        </form>
      </div>
    );
  }
}
export default SketchingForm;
//SketchingList.js//
import React, { Component } from 'react';
import {
  subscribeToSketchings,
} from './api';


class SketchingList extends Component {
  constructor(props) {
    super(props);

    subscribeToSketchings((sketching) => {
      this.setState(prevState => ({
        sketchings: prevState.sketchings.concat([sketching]),
      }));
    });
  }

  state = {
    sketchings: [],
  };

  render() {
    const sketchings = this.state.sketchings.map(sketching => (
      <li
        className="SketchingList-item"
        key={sketching.id}
      >
        {sketching.name}
      </li>
    ));

    return (
      <ul
        className="SketchingList"
      >
        {sketchings}
      </ul>
    );
  }
}

export default SketchingList;

Curriculum

Project Repository

Thank you

Sort:  

Thank you for your contribution.
While I liked the content of your contribution, I would still like to extend one advice for your upcoming contributions:

  • In the source code section you could put a brief summary of each file.

Looking forward to your upcoming tutorials.

Your contribution has been evaluated according to Utopian rules and guidelines, as well as a predefined set of questions pertaining to the category.

To view those questions and the relevant answers related to your post,Click here


Need help? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]

Hey @engr-muneeb
Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!

Contributing on Utopian
Learn how to contribute on our website or by watching this tutorial on Youtube.

Want to chat? Join us on Discord https://discord.gg/h52nFrV.

Vote for Utopian Witness!