skip to Main Content

I am trying to get started with Node.js. I have created a simple page:

'use client';
import React, { useEffect, useState } from 'react';
import axios from 'axios';

function callApi() {
  const [data, setData] = useState('');

  useEffect(() => {
    axios.post('http://localhost:3001/api/data', {firstName: 'Fred', surname: 'Bloggs'}, {headers: {'Content-Type': 'multipart/form-data'}})
      .then(response => {
        setData(response.data.message);
      })
      .catch(error => {
        console.error(error);
      });
  }, []);

  return (
    <div className="App">
      <h1>React and Node.js Integration</h1>
      <p>Message from the server: {data}</p>
    </div>
  );
}

export default callApi;

and a simple Node.js endpoint that handles post requests:

const express = require('express');
const app = express();
const port = 3001; 
const multer = require('multer');
const upload = multer();

app.post('/api/data', upload.none(), (req, res) => {
  const formData = req.body;
  console.log('form data', formData);
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET,POST');
  res.setHeader('Access-Control-Max-Age', 2592000); // 30 days
  const data = "Response from POST request: " + formData.firstName + " " + formData.surname
  res.json(data)
});

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
}); 

I don’t think this is working properly because (1) Fred Bloggs is printed to the Terminal twice in Node.js even though there is only one call to console.log as shown below:

enter image description here

Also ‘Response from POST request:Fred Bloggs’ is not returned to the client.

3

Answers


  1. You might be seeing "Fred Bloggs" printed twice because your browser could be sending an additional preflight request. When CORS is involved, browsers often send an OPTIONS request before the actual POST request. This preflight request does not contain the form data, but your server may still be logging it.

    You can update your Node.js code to handle the OPTIONS request separately and avoid logging it.

    You are not sending the response correctly in your res.json call. The res.json() method expects an object, but you are passing a string.

    Updated code

    'use client';
    import React, { useEffect, useState } from 'react';
    import axios from 'axios';
    
    function callApi() {
      const [data, setData] = useState('');
    
      useEffect(() => {
        axios.post('http://localhost:3001/api/data', { firstName: 'Fred', surname: 'Bloggs' }, {
          headers: { 'Content-Type': 'multipart/form-data' }
        })
          .then(response => {
            setData(response.data.message);  // Accessing the message field from the response object
          })
          .catch(error => {
            console.error(error);
          });
      }, []);
    
      return (
        <div className="App">
          <h1>React and Node.js Integration</h1>
          <p>Message from the server: {data}</p>
        </div>
      );
    }
    
    export default callApi;
    

    Node.js endpoint

    const express = require('express');
    const multer = require('multer');
    const cors = require('cors');
    const app = express();
    const port = 3001;
    
    const upload = multer();
    
    app.use(cors());  // Allow all CORS requests
    
    // Handle the OPTIONS request
    app.options('/api/data', (req, res) => {
      res.setHeader('Access-Control-Allow-Origin', '*');
      res.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET, POST');
      res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
      res.setHeader('Access-Control-Max-Age', 2592000); // 30 days
      res.sendStatus(204); // No Content
    });
    
    app.post('/api/data', upload.none(), (req, res) => {
      const formData = req.body;
      console.log('Form data:', formData);
      const responseMessage = "Response from POST request: " + formData.firstName + " " + formData.surname;
      res.json({ message: responseMessage }); // Send response as an object
    });
    
    app.listen(port, () => {
      console.log(`Server is running on port ${port}`);
    });
    
    Login or Signup to reply.
  2. The issue you are experiencing with is related React Development mode – not NodeJS itself.

    The primary reason for useEffect executing twice is the strict mode in React’s development mode. This mode intentionally invokes lifecycle methods and effects multiple times to help identify problems. It’s a feature designed to aid developers, not a bug. For example, it is common to subscribe events in useEffect and forget to clean them on unmount, which causes a memory leak. Double invocation in dev mode helps to identify such mistakes at an early stages.

    It only happens in Dev mode not in Prod build.

    It might be confusing at first but I don’t see much problem sending 2 requests in dev mode.

    Here is more details explained by React Team. Info

    Login or Signup to reply.
  3. Since React 18, the Strict mode provoke api call to be triggered twice to ensure a correct behaviour of your application. You can try disabling it to be sure. In a basic react app, you should find something like :

    createRoot(document.getElementById('root')!).render(
      <StrictMode>
        <App />
      </StrictMode>
    );
    

    Remove or comment the StrictMode component And check again.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search