Creating a Basic User Interface with ReactJS

0 Shares
0
0
0
0

Introduction

We will build a simple chat application with ReactJS based on the server we have already implemented. We start by creating static components, which are then connected to the state. In the final step, we subscribe to the event server.

Prerequisites
  • Basic knowledge of JavaScript and ReactJS (I'm trying to keep things simple)
  • npm v6
  • npx v6
  • node v12

Step 1: Launch the project

In your project folder, run npx create-react-app --use-npm simple-chat-app and wait for the installation to finish. This will add all the necessary dependencies and provide a simple development and build experience.

Add the following files to the src folder:

  • App.js (should already exist)
  • ChatLineHolder.js
  • ChatLine.js
  • ChatInput.js

Run npm run start from the project folder in your console. This will start a development server and open the application in your default browser.

There should already be an App.js file in your src folder. For now, you can delete everything it returns and just have one. <div> Return it.

const App = () => {
return <div className='chat-app'></div>
}

We start by creating our components from the bottom up.

Step 2: Component

In ChatLine.js we create a new component that has two properties:

  • name: string
  • message: string

This is the data we will later receive from the server as event data.

Component One

  • It simply returns and displays the name and message:

    import React from 'react'
    const ChatLine = ({ name, message }) => {
    return (
    <li>
    {name}: {message}
    </li>
    )
    }
    export default

    Note: ReactJS automatically prevents html injection, so you don't need to worry about it.

    Step 3: Component

    In ChatHolder.js we create a new component that takes only one base:

    Lines: Array<{ name: string, message: string }>

    If lines If empty, we display a simple notification.

    if (lines.length === 0) {
    return <div>Chat history is empty</div>
    }

    Lines is an array of objects. Each object has a name and a property message. We map over all the items in the array to get the result by passing the name and string to the component. Let's create multiple ChatLine nodes.

    const chatLines = lines.map(
    (line, index) => (
    <ChatLine key={index} name={line.name} message={line.message} />
    )
    )

    Then the result in a

      We render and are left with the finished component:

      import React from 'react'
      import ChatLine from './ChatLine'
      const ChatLineHolder = ({ lines }) => {
      if (lines.length === 0) {
      return <div>Chat history is empty</div>
      }
      const chatLines = lines.map(
      (line, index) => (
      <ChatLine key={index} message={line.message} name={line.name} />
      )
      )
      return (
      <ul>
      {chatLines}
      </ul>
      )
      }
      export default ChatLineHolder

      Note: You usually use a unique identifier as a key. In this case, we can safely use index, because the association between index and object does not change. In a real-world application, this is very frustrating.

      Step 4: Component

      As you may have noticed, there is still no way to import messages. Handles text input and uses a callback function on submit to launch a new chat message on button click. Requires a prop:

      • onSend: (message: string) => void

      The user input component keeps its state and manages updates.

      const [value, setValue] = useState('')
      const onChange = (event) => {
      setValue(event.target.value)
      }

      When the submit button is clicked, the onSend function is called with the current state. Since we don't want the user to have to manually clear the input field, we reset the state after calling onSend.

      const onClickSend = () => {
      onSend(value)
      setValue('')
      }
      

      Here is the final component:

      import React, { useState } from 'react'
      const ChatInput = ({ onSend }) => {
      const [value, setValue] = useState('')
      const onChange = (event) => {
      setValue(event.target.value)
      }
      const onClickSend = () => {
      setValue('')
      onSend(value)
      }
      return (
      <div>
      <input type='text' value={value} onChange={onChange} />
      <button onClick={onClickSend}>send</button>
      </div>
      )
      }
      export default ChatInput

      Step 5: Component

      Mode

      Since our components don't store any data, As the main component we use to manage state. We have 3 types of states:

      • name: The name of the user who is chatting.
      • chatLines: An array representing the chat history.
      • eventLister: Used to manage events from the server. (Note: I decided to put the listener in the part.

      (For simplicity's sake, you might want to take a different approach in a real-world application.)

      We use state hooks to initialize our state:

       const [chatLines, setChatLines] = useState([])
      const [eventListener, setEventListener] = useState(null)
      const [name] = useState(generateRandomName())

      Component rendering test

      For now we just display our name and the component We present the chatLines with fixed items. Since chatLines is an empty array, our notification should be provided. We call an empty flash function to We will send it. Execution will follow in the next step.

      return (
      <div className='chat-app'>
      <h2>Chatting as {name}</h2>
      <ChatLineHolder lines={chatLines} />
      <ChatInput onSend={() => {}} />
      </div>
      )

      If you want to check if your implementation works with some data, just add a few items to chatLines.

      const [chatLines, setChatLines] = useState([{ name: 'Thomas', 'message': 'Hi' }])
      

      Add interaction

      We can already enter text into our chat input field, but pressing send does nothing. This is because we have a function called We sent it that does absolutely nothing. So we need an implementation for it.

      Since we are not connected to our server yet, we will implement our onSend in a way that directly updates the state. For that we will add a helper function addChatLine: (chatLine) => void. We will use it later to handle message events sent by the server.

      const addChatLine = (chatLine) => {
      setChatLines(chatLines => {
      return [...chatLines, chatLine]
      })
      // If you don't have a lot of experience with ReactJS, just think of this as:
      // setChatLines([...chatLines, chatLine])
      }

      onSend creates a chat line object and calls addChatLine

      const onSend = (message) => {
      const chatLine = { name, message }
      addChatLine(chatLine)
      }

      Don't forget to send to Send.

      return (
      <div className='chat-app'>
      <h2>Chatting as {name}</h2>
      <ChatLineHolder lines={chatLines} />
      <ChatInput onSend={onSend} />
      </div>
      )

      You should now be able to enter text in the input field, click send, and see your message instantly.

      Step 6: Connect to the server

      Chatting with yourself is very boring. That's why we need to connect our application to the server. This happens in 2 parts:

      • Send message to server
      • Subscribe to events sent by the server
      Send message to server

      We need to modify the onSend function we wrote. We can replace the entire body with a simple fetch call, which sends our name and message in the body. It is important not to call addChatLine because we will do that later when we receive the event sent by the server.

      const onSend = async (message) => {
      fetch('http://localhost:4000/say',
      {
      headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      },
      method: 'post',
      mode: 'no-cors',
      body: `name=${name}&message=${message}`
      })
      }

      If you click the submit button now, a request will be sent to the server but your status will not be updated yet! We will address this in the next step.

      Subscribe to events sent by the server

      Before we start listening to events, we need to have a function that handles such message events. Since we will be connecting to message type events later, we can implement it accordingly. All it does is take event.data, parse it into an object, and call addChatLine.

      const onMessage = (event) => {
      const chatLine = JSON.parse(event.data)
      addChatLine(chatLine)
      }
      

      To receive events sent by the server, we create an object of the EventSource class with our local address. Then we add an event listener for the message type. Then we wrap it in a useEffect hook (this ensures that when the component It mounts, the event source is initialized, and then it is closed after it is mounted).

      useEffect(() => {
      let source = new EventSource('http://localhost:4000/listen')
      source.addEventListener('message', onMessage)
      setEventSource(source)
      return () => { source.close() }
      }, [])

      Component Finally, it looks like this:

      import React, { useEffect, useState } from 'react'
      import ChatLineHolder from './ChatLineHolder'
      import ChatInput from './ChatInput'
      const App = () => {
      const [chatLines, setChatLines] = useState([])
      const [eventSource, setEventSource] = useState(null)
      const [name] = useState(generateRandomName())
      const addChatLine = (chatLine) => {
      setChatLines(chatLines => [...chatLines, chatLine])
      }
      const onMessage = (event) => {
      const chatLine = JSON.parse(event.data)
      addChatLine(chatLine)
      }
      useEffect(() => {
      let source = new EventSource("http://localhost:4000/listen")
      source.addEventListener('message', onMessage)
      setEventSource(source)
      return () => { source.close() }
      }, [])
      const onSend = (message) => {
      fetch(
      `http://localhost:4000/say`,
      {
      headers: {
      "Content-Type": "application/x-www-form-urlencoded",
      },
      method: "post",
      mode: 'no-cors',
      body: `name=${name}&message=${message}`,
      })
      }
      return (
      <div className='chat-app'>
      <h2>Chatting as {name}</h2>
      <ChatLineHolder lines={chatLines} />
      <ChatInput onSend={onSend} />
      </div>
      )
      }
      export default App

      Result

      Now you can open your page in two tabs and chat with two alter egos! You can also mount the App component twice (by changing index.js), because each instance has its own state.

  • Leave a Reply

    Your email address will not be published. Required fields are marked *

    You May Also Like