DEV Community

Cover image for Full-Stack React & Node.js - Get client & server talking
Dave
Dave

Posted on

Full-Stack React & Node.js - Get client & server talking

Finally, the fun part is here! All our components are in place and now we only need to change 1 component to get the server and client talking.

In react-client, edit AddEditNote.js, and replace the contents with this:

import React, {useState, useEffect} from 'react';
import RenderData from "./RenderData";
import Form from './Form';

const AddEditNote = () => {
  const [note, setNote] = useState({});

  useEffect( () => {
    async function fetchData() {
      console.log('Calling fetch...')
      const response = await fetch('http://localhost:4011/note');

      if (response.ok) {
        console.log('Response received from server and is ok!')
        const {note} = await response.json();

        setNote(note)
      }
    }

    fetchData()
  }, [])

  return (
    <div>
      <RenderData
        data={note}
      />
      <Form
        entity={note}
      />
    </div>
  );
};

export default AddEditNote;
Enter fullscreen mode Exit fullscreen mode

To test this you will need 2 shell/CLI programs.

Use one to run the server, in node-server folder, execute:

npm run start
Enter fullscreen mode Exit fullscreen mode

The second in react-client folder, execute:

npm run start
Enter fullscreen mode Exit fullscreen mode

Now, when your React site runs, and you click on "Edit Post" you will see a form with data that has been loaded from the server.

If you take a look at the shell/CLI where you ran your Node.js server you should see the requests logged by morgan.

There's a problem here though!

node server request log

Each time we visit the form we see two GET requests on the server.

If you open the dev console in Chrome with F12 and go to the network tab you will also see duplicated requests here!

Chrome network tab

Pro tip: if your Chrome network tab doesn't show what you expect, make sure the correct category is selected. Look at the second row in the image above and you will see "Fetch/XHR" selected.

Let's check the Chrome console to see what we've logged out:

Chrome console logs

Sure enough, we call fetch twice, and receive a response twice. What is going on here?

This kind of thing is quite common, and in apps with a very complex structure of many nested components, you might see the same requests repeated 10, 12 or more times.

This is due to the way React will re-render components when either the props or the state changes. Which, of course is exactly what React is supposed to do. As developers we need to be careful when we fetch data inside, what is essentially, UI rendering code.

So, are we doomed? Do we just give up and go home?

Never Give Up toddler meme

To fix this we need to keep track of requests that have been made, and cancel them when the component unmounts, and ignore any responses that might sometimes return from cancelled requests.

Edit AddEditNote.js again, to this code:

import React, {useState, useEffect} from 'react';
import RenderData from "./RenderData";
import Form from './Form';

const AddEditNote = () => {
  const [note, setNote] = useState({});

  useEffect( () => {
    const abortController = new AbortController();

    async function fetchData() {
      console.log('Calling fetch...')
      try {
        const response = await fetch('http://localhost:4011/note', {
          signal: abortController.signal,
        });

        if (response.ok) {
          console.log('Response received from server and is ok!')
          const {note} = await response.json();

          if (abortController.signal.aborted) {
            console.log('Abort detected, exiting!')
            return;
          }

          setNote(note)
        }
      } catch(e) {
        console.log(e)
      }
    }

    fetchData()

    return () => {
      console.log('Aborting GET request.')
      abortController.abort();
    }
  }, [])

  return (
    <div>
      <RenderData
        data={note}
      />
      <Form
        entity={note}
      />
    </div>
  );
};

export default AddEditNote;
Enter fullscreen mode Exit fullscreen mode

We use the AbortController as a signal passed as an option to the fetch request. When the component unmounts we abort any requests. And, just in case a request slipped through before being aborted, we check the abort state when a request returns.

This bug is common in production code bases. With each user action triggering multiple fetch requests causing the server to make multiple DB requests, your servers might end up with 10 times the load they should have.

If you save and run the code again, you will see that duplicate requests are cancelled and we only hit the server with a single GET request each time we visit the form. Good job - bug fixed!

In the next part, we will explore other HTTP verbs to make data mutations on the server...

Code repo: Github Repository

Discussion (2)

Collapse
srishtikprasad profile image
Srishti Prasad

Nice explanation Dave!!

Collapse
neohed profile image
Dave Author

Thank you :)