DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Coding
  3. JavaScript
  4. Building a React-Based Chat Client With Redux Part 1: React and React DOM

Building a React-Based Chat Client With Redux Part 1: React and React DOM

In Part 1, we go over React and the ReactDOM, showing how they can used to make your JavaScript code better. Read on to get started!

Cliff Hall user avatar by
Cliff Hall
·
Jul. 15, 18 · Tutorial
Like (2)
Save
Tweet
Share
10.22K Views

Join the DZone community and get the full member experience.

Join For Free
just two users casually chatting, thanks to react and redux!
let's build a non-trivial app with react and then refactor it to use redux!

much of the advice you get regarding the addition of redux to your react projects is to only do so once they reach a certain size, because of the extra complexity redux adds. that's certainly fair. but it will leave you with a bit of technical debt (refactoring to be done later) that you wouldn't have if you just started out with react and redux.

consequently, i thought it might be nice to present an exercise where we do just that: build an app as simply as possible using react and reactdom alone (not even jsx since you need more dependencies and a build process to support that), and then refactor to use jsx and redux.

in a previous post , i described my node-based multi-server chat project, which had a rudimentary javascript client built in. though all of the system functionality could be tested with several instances of that client, it was lacking in a number of ways:

  • it only allowed you to connect with two predefined users (this one provides freeform username input, so any number can connect).

  • it didn't show scrollable message history, although you could see the latest message sent or received on one line.

  • it didn't show you a list of all other users connected to the server mesh, it just logged the user list to the console whenever the server updated the client.

  • it didn't alert you when your selected recipient had disconnected or reconnected.

  • it didn't support message threads (this client automatically shows the appropriate thread and selects the recipient after an incoming message is received).

and as minimal as it was, that client was right at the edge of complexity where a framework of some sort would be welcome. so, in this post, i'm going to build a more fully featured client to communicate with that same chat server.

the first cut: react and reactdom

in the most minimalistic react project, you just pull in react and reactdom, then use react.createelement() to render your components (which extend react.component). you can see the first version of the project on its 1.1.0 tag .

in that version of the project, the html template looks like this:

react-chat-client.html

<!doctype html>
<html>
<head>
    <title>react chat client</title>
    <script src="https://unpkg.com/react@16.4.1/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16.4.1/umd/react-dom.development.js"></script>
    <script src="https://cdn.socket.io/socket.io-1.4.5.js"></script>
</head>
<body>
<div id="app"></div>
<script type="module" src="/main.js"></script>
</body>
</html>

note how it uses script tags to pull in its dependencies, react, reactdom, and socket.io, from cdn urls, and the main.js file that is the entry point to the app. this allows us to serve the app with a simple express server like this:

client-server.js

// serve the client
const express = require('express');
const app = express();
const path = require('path');
const port = 8080;

console.log(`server for react instant message client started. http://localhost:${port}/`);
app.use(express.static('app'));

app.get('/', function(req, res) {
    console.log('new client request');
    res.sendfile(path.join(__dirname + '/react-chat-client.html'));
});

app.listen(port);

that sidesteps a lot of mental friction associated with react toolchain setup. no need for create-react-app, babel, webpack, etc.

application structure

in this first cut, the app code only consisted of four files, tucked away in one folder.

you've already seen two of them above, client-server.js and react-chat-client.html . the other two are:

  • main.js

  • client.js

main.js

this file couldn't be simpler and just rendered the client class as a dom element replacing the 'app' element in the html template. recall it was referred to in a script tag in the body of the template.

import { client } from './client.js'

function render() {
    reactdom.render(
        react.createelement(client),
        document.getelementbyid('app')
    )
}
render();

client.js

this one is monolithic, weighing in at exactly six-hundred lines. i won't duplicate it here, but it's worth taking a look at in the repo . monolithic classes are hard to maintain and reason about, particularly if there is more than one person on the team.

this file defines all the protocols, styles as css-in-js, and so forth in constants that all components in the file can see. it also defines all the components, and, without hoisting, they have to be defined before they are referred to, so the client component is defined last and all the others come before it, and so on, with nested components. this non-obvious ordering requirement is another reason monolithic files are bad.

react.createelement vs jsx

one thing i will point out here is how, even though it is possible to do react without jsx, your render methods look like this:

    render() {
        return createelement('div', {style: clientstyle},

            // user selector
            createelement(userinput, {
                connected: this.state.connected,
                onchange: this.onuserchange
            }),

            // port selector
            createelement(portselector, {
                connected: this.state.connected,
                onchange: this.onportchange
            }),

            // recipient selector
            createelement(recipientselector, {
                users: this.state.users,
                recipient: this.state.recipient,
                onchange: this.onrecipientchange
            }),

            // outgoing message input and send button
            createelement(messagetransport, {
                connected: this.state.connected,
                recipient: this.state.recipient,
                outgoingmessage: this.state.outgoingmessage,
                onchange: this.onmessageinputchange,
                onsend: this.onsendmessage
            }),

            // message history
            createelement(messagehistory, {
                user: this.state.user,
                messages: this.state.messages,
                connected: this.state.connected
            }),

            // footer (status line / connection toggle)
            createelement('div', {style: footerstyle},

                // status line
                createelement(statusline, {
                    status: this.state.status,
                    iserror: this.state.iserror
                }),

                // connect button
                createelement(connectbutton, {
                    enabled: (this.state.port && this.state.user),
                    connected: this.state.connected,
                    handleclick: this.ontoggleconnection
                })
            )
        )
    }

instead of this one, from version 1.1.0 where jsx was added:

   render() {
        return <div style={clientstyle}>

            <userinput connected={this.state.connected} onchange={this.onuserchange}/>

            <portselector connected={this.state.connected} onchange={this.onportchange}/>

            <recipientselector users={this.state.users}
                               recipient={this.state.recipient}
                               onchange={this.onrecipientchange}/>

            <messagetransport connected={this.state.connected}
                              recipient={this.state.recipient}
                              outgoingmessage={this.state.outgoingmessage}
                              onchange={this.onmessageinputchange}
                              onsend={this.onsendmessage}/>

            <messagehistory user={this.state.user}
                            messages={this.state.messages}
                            connected={this.state.connected}/>

            <footer status={this.state.status}
                    iserror={this.state.iserror}
                    connectenabled={(this.state.port && this.state.user)}
                    connected={this.state.connected}
                    handletoggle={this.ontoggleconnection}/>

        </div>;
    }

note: this is a bit of an improvement, but i assure you we're going to vastly improve on that render function by the end of part 2.

data flow

the basic premise of data flow in this redux-less version is that the top-level client component initializes all the state in its constructor:

        this.state = {
            connected: false,
            status: 'select a user and port.',
            iserror: false,
            user: null,
            recipient: no_recipient,
            outgoingmessage: '',
            messages: [],
            port: ports[0],
            users: []
        };

and then passes the bits of state and callbacks that modify it down to the child components as props, like so:

            // user input field
            createelement(userinput, {
                connected: this.state.connected,
                onchange: this.onuserchange
            }),

in this case, the onuserchange callback being passed to the connectbutton instance looks like:

   // the user input field has changed
    onuserchange(user) {
        this.setstate({user: user});
    }

so when the userinput component's text input field changes, triggering a call to onuserchange , the client component changes the state, causing react to re-render.

that's all easy enough, and it's nice that react takes care of re-rendering when state changes. but in the real world, this approach doesn't scale. components need to go into their own files, jsx is really much easier for us to work with, and most teams like some variant of the flux pattern for managing application wide state, because it's easier to modularize and extend as a project grows.

that's all for part 1. now, onward to part 2 , where we refactor to use redux and bindings!

here again, for reference, is the first cut of the project:

  • react and reactdom only ( version 1.0.0 )

React (JavaScript library)

Published at DZone with permission of Cliff Hall, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Microservices Testing
  • DeveloperWeek 2023: The Enterprise Community Sharing Security Best Practices
  • 10 Most Popular Frameworks for Building RESTful APIs
  • Running Databases on Kubernetes

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: