skip to Main Content

I have two files Profile.js and Controller.js as below. When I access link localhost/profile, I saw two additional POST request are sent from front-end to back-end.

On further debugging I found, useEffect hooks present in Controller.js file are being executed when I access the link localhost/profile. Is it normal or I am doing something wrong here?

Note
Please do not close this question. This is not same as why useEffect is called twice. useEffect called twice within same component is one thing, here useEffect of other components are executed. These two are different in nature.

At the end, I have shown how these components are included in App.js file.

Profile.js

import react, { useState, useEffect } from "react";
import axios from "axios";
const lm = require("./lm.js");

const Profile = () => {
  const [firstName, setFirstName] = useState(null);
  const [lastName, setLastName] = useState(null);
  const [email, setEmail] = useState(null);
  const [skype, setSkype] = useState(null);
  const [mobile, setMobile] = useState(null);
  const [dob, setDob] = useState(null);
  const [created, setCreated] = useState(null);
  const [gender, setGender] = useState(null);

  const fetchProfile = async () => {
    var user = null;
    const userData = localStorage.getItem("user");

    if (userData) {
      user = JSON.parse(userData);

      try {
        await axios
          .post(
            "/profile",
            { email: user.email },
            {
              headers: {
                Authorization: user.jwt,
              },
            },
          )
          .then(function (res) {
            let obj = JSON.parse(res.data);
            setFirstName(obj.firstname);
            setLastName(obj.lastname);
            setMobile(obj.mobile);
            setEmail(obj.email);
            setSkype(obj.skype);
            setDob(new Date(obj.dob).toLocaleDateString());
            setCreated(new Date(obj.created).toLocaleDateString());
            setGender(lm.getGender(obj.gender));
          });
      } catch (err) {
        console.log(err);
        window.location.href = "/authrequired";
      }
    } else {
      console.log("User not logged in");
    }
  };

  useEffect(() => {
    fetchProfile();
  }, []);

  return (
    <div className="medium-text">
      <div className="center">
        <h2>User profile</h2>
        <table id="tbl">
          <tbody>
            <tr>
              <th>Fields</th>
              <th>Values</th>
            </tr>
            {firstName && (
              <tr>
                <td>First name</td>
                <td>{firstName}</td>
              </tr>
            )}
            {lastName && (
              <tr>
                <td>Last name</td>
                <td>{lastName}</td>
              </tr>
            )}
            {email && (
              <tr>
                <td>Email Id</td>
                <td>{email}</td>
              </tr>
            )}
            {mobile && (
              <tr>
                <td>Mobile</td>
                <td>{mobile}</td>
              </tr>
            )}
            {skype && (
              <tr>
                <td>Skype</td>
                <td>{skype}</td>
              </tr>
            )}
            {dob && (
              <tr>
                <td>Date of birth</td>
                <td>{dob}</td>
              </tr>
            )}
            {created && (
              <tr>
                <td>Created on</td>
                <td>{created}</td>
              </tr>
            )}
            {gender && (
              <tr>
                <td>Gender</td>
                <td>{gender}</td>
              </tr>
            )}
          </tbody>
        </table>
      </div>
    </div>
  );
};

export default Profile;

Controller.js

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import { Link } from "react-router-dom";
import axios from "axios";
const lm = require("../../routes/lm.js");

const AddController = () => {
  const [status, setStatus] = useState(null);

  const handleSubmit = async (
    name,
    ipaddr,
    port,
    sshuser,
    sshpass,
    sshport,
    license,
  ) => {
    setStatus(null);

    const userData = localStorage.getItem("user");

    if (userData != null) {
      var user = JSON.parse(userData);

      try {
        const res = await axios
          .post(
            "/exec/addcontroller",
            {
              email: user.email,
              name,
              ipaddr,
              port,
              sshuser,
              sshpass,
              sshport,
              license,
            },
            {
              headers: {
                Authorization: user.jwt,
              },
            },
          )
          .then(function (res) {
            if (res.status === 200) {
              setStatus({
                type: "success",
                message: `Controller ${name} added successfully`,
              });
            } else {
              setStatus({
                type: "error",
                message: `Could not add controller ${name}`,
              });
            }
          });
      } catch (err) {
        setStatus({
          type: "error",
          message: err.message,
        });
      }
    } else {
      setStatus({
        type: "error",
        message: "User not logged in",
      });
    }
  };

  let out = (
    <form
      onSubmit={(event) => {
        event.preventDefault();
        const [name, ipaddr, port, sshuser, sshpass, sshport, license] =
          event.target.elements;

        handleSubmit(
          name.value,
          ipaddr.value,
          port.value,
          sshuser.value,
          sshpass.value,
          sshport.value,
          license.value,
        );
      }}
    >
      <div className="medium-text">
        <div className="center">
          <h2>Add Controller</h2>
          <table id="">
            <tbody>
              <tr>
                <td>
                  <b>Controller</b>
                  <br />
                  <input
                    required
                    type="text"
                    maxLength={15}
                    id="name"
                    name="name"
                    placeholder="Unique name"
                  />
                </td>
                <td>
                  <b>IP Address</b>
                  <br />
                  <input
                    required
                    type="text"
                    maxLength={32}
                    id="ipaddr"
                    name="ipaddr"
                    placeholder="Machine IP address"
                  />
                </td>
                <td>
                  <b>Port</b>
                  <br />
                  <input
                    required
                    type="text"
                    maxLength={5}
                    id="port"
                    name="port"
                    placeholder="Listen port"
                  />
                </td>
              </tr>

              <tr>
                <td>
                  <b>SSH User</b>
                  <br />
                  <input
                    required
                    type="text"
                    maxLength={15}
                    id="sshuser"
                    name="sshuser"
                    placeholder="Machine SSH user"
                  />
                </td>
                <td>
                  <b>SSH Password</b>
                  <br />
                  <input
                    required
                    type="password"
                    maxLength={15}
                    id="sshpass"
                    name="sshpass"
                    autoComplete="on"
                    placeholder="Machine SSH password"
                  />
                </td>
                <td>
                  <b>SSH Port</b>
                  <br />
                  <input
                    required
                    type="text"
                    maxLength={5}
                    id="sshport"
                    name="sshport"
                    placeholder="SSH Listen port"
                  />
                </td>
              </tr>
            </tbody>
          </table>

          <table id="">
            <tbody>
              <tr>
                <td>
                  <b>License</b>
                  <br />
                  <input
                    required
                    type="text"
                    maxLength={24}
                    id="license"
                    name="license"
                    placeholder="License file name"
                  />
                </td>
              </tr>
            </tbody>
          </table>

          <br />
          <div className="">
            <button type="submit">Submit</button>
          </div>

          <br />
          {status && (
            <div className={`alert ${status.type}`}>{status.message}</div>
          )}
        </div>
      </div>
    </form>
  );

  return out;
};

const EditOneController = () => {
  const initState = [
    { name: "", ipaddr: "", port: 0, sshuser: "", sshport: 0, license: "" },
  ];

  const [state, setState] = useState(initState);
  const [status, setStatus] = useState(null);

  var user = null;
  const userData = localStorage.getItem("user");

  if (userData != null) {
    user = JSON.parse(userData);
  }

  const handleSubmit = async (
    name,
    ipaddr,
    port,
    sshuser,
    sshpass,
    sshport,
    license,
  ) => {
    setStatus(null);
    if (user != null) {
      try {
        const res = await axios
          .post(
            "/exec/editonecontroller",
            {
              email: user.email,
              name,
              ipaddr,
              port,
              sshuser,
              sshpass,
              sshport,
              license,
            },
            {
              headers: {
                Authorization: user.jwt,
              },
            },
          )
          .then(function (res) {
            if (res.status === 200) {
              setStatus({
                type: "success",
                message: `Controller ${name} edited successfully`,
              });
            } else {
              setStatus({
                type: "error",
                message: `Could not edit controller ${name}`,
              });
            }
          });
      } catch (err) {
        console.log(err);
        setStatus({
          type: "error",
          message: err.message,
        });
      }
    } else {
      setStatus({
        type: "error",
        message: "User not logged in",
      });
    }
  };

  const name = lm.getURLQueryVariable("name");
  const fetchController = async () => {
    try {
      await axios
        .post(
          "/editonecontroller/data/?name=" + name,
          { email: user.email },
          {
            headers: {
              Authorization: user.jwt,
            },
          },
        )
        .then(function (res) {
          //console.log(res);

          if (res.status === 200) {
            const newState = res.data.flatMap(
              ({ name, ipaddr, port, sshuser, sshpass, sshport, license }) => [
                { name, ipaddr, port },
                { sshuser, sshpass, sshport },
                { license },
              ],
            );

            setState(newState);
          } else {
            setStatus({
              type: "error",
              message: `Could not fetch controller ${name} data`,
            });
          }
        });
    } catch (err) {
      setStatus({
        type: "error",
        message: err.message,
      });
    }
  };

  useEffect(() => {
    fetchController();
  }, []);

  const labelArr = [
    "Controller",
    "IP Address",
    "Port",
    "SSH User",
    "SSH Password",
    "SSH Port",
    "License",
  ];

  var labelIndex = 0;

  let out = (
    <form
      onSubmit={(event) => {
        event.preventDefault();
        const [name, ipaddr, port, sshuser, sshpass, sshport, license] =
          event.target.elements;

        handleSubmit(
          name.value,
          ipaddr.value,
          port.value,
          sshuser.value,
          sshpass.value,
          sshport.value,
          license.value,
        );
      }}
    >
      <div className="medium-text">
        <div className="center">
          <h2>Edit Controller</h2>
          <table id="">
            <tbody>
              {state.map((item, index1) => (
                <tr key={index1}>
                  {Object.keys(item).map((key, index2) => (
                    <td key={labelIndex++}>
                      <b>{labelArr[labelIndex]}</b>
                      <br />
                      <input
                        required
                        type={labelIndex == 4 ? "password" : "text"}
                        id={key}
                        name={key}
                        onChange={(e) => {}}
                        defaultValue={item[key]}
                      />
                    </td>
                  ))}
                </tr>
              ))}
            </tbody>
          </table>
          <br />
          <div className="">
            <button type="submit">Submit</button>
          </div>

          <br />
          {status && (
            <div className={`alert ${status.type}`}>{status.message}</div>
          )}
        </div>
      </div>
    </form>
  );

  return out;
};

const EditController = () => {
  const initState = [
    { name: "", ipaddr: "", port: 0, sshuser: "", sshport: 0, license: "" },
  ];

  const [state, setState] = useState(initState);
  const [status, setStatus] = useState(null);

  var user = null;
  const userData = localStorage.getItem("user");

  if (userData != null) {
    user = JSON.parse(userData);
  }

  const fetchControllers = async () => {
    setStatus(null);

    if (user != null) {
      try {
        const res = await axios
          .post(
            "/exec/editcontroller",
            { email: user.email },
            {
              headers: {
                Authorization: user.jwt,
              },
            },
          )
          .then(function (res) {
            if (res.status === 200) {
              setState(res.data);
            } else {
              setStatus({
                type: "error",
                message: "Error in displaying data",
              });
            }
          });
      } catch (err) {
        setStatus({
          type: "error",
          message: err.message,
        });
      }
    } else {
      setStatus({
        type: "error",
        message: "User not logged in",
      });
    }
  };

  useEffect(() => {
    fetchControllers();
  }, []);

  function editSingleControllerLink(val) {
    return "/components/editonecontroller?name=" + val.val;
  }

  return (
    <div className="medium-text">
      <div className="center">
        <h2>Controllers</h2>
        <table id="tbl">
          <thead>
            <tr>
              <th>Name</th>
              <th>IP Address</th>
              <th>Port</th>
              <th>SSH User</th>
              <th>SSH Port</th>
              <th>License</th>
            </tr>
          </thead>
          <tbody>
            {state.map((item, index) => (
              <tr key={index}>
                {Object.values(item).map((val, index) =>
                  index === 0 ? (
                    <td key={index}>
                      {" "}
                      <a href={editSingleControllerLink({ val })}>{val}</a>{" "}
                    </td>
                  ) : (
                    <td key={index}>{val}</td>
                  ),
                )}
              </tr>
            ))}
          </tbody>
        </table>

        <br />
        {status && (
          <div className={`alert ${status.type}`}>{status.message}</div>
        )}
      </div>
    </div>
  );
};
export default { AddController, EditController, EditOneController };

And these components are included from App.js file as below.

import { Routes, Route } from 'react-router-dom';

import Home from '../routes/Home';
import Login from '../routes/Login';
import Register from '../routes/Register';
import Profile from '../routes/Profile';
import Logout from '../routes/Logout';
import AuthReqd from '../routes/AuthReqd';

import controller from '../modules/controller/controller.js'


const App = () => {
        
  return (
    <>
      <Routes>
        <Route path="/" element={<Layout />}>
          <Route index element={<Home />} />
          <Route path="components/addcontroller" element={controller.AddController()} />
          <Route path="components/editcontroller" element={controller.EditController()} />
          <Route path="components/editonecontroller" element={controller.EditOneController()} />
          
          <Route path="login" element={<Login />} />
          <Route path="register" element={<Register />} />
          <Route path="profile" element={<Profile />} />
          <Route path="logout" element={<Logout />} />
          <Route path="authrequired" element={<AuthReqd />} />

          <Route path="*" element={<p>Not found!</p>} />
        </Route>
      </Routes>
    </>
  );
};

export default App;

2

Answers


  1. This line in Controller.js is problematic

    export default { AddController, EditController, EditOneController };
    

    You’re exporting multiple components as a single object which requires you to use an import like

    import controllers from './Controller';
    

    which I can only assume led you to the following usage

    <Route path="components/addcontroller" element={controller.AddController()} />
    <Route path="components/editcontroller" element={controller.EditController()} />
    <Route path="components/editonecontroller" element={controller.EditOneController()} />
    

    In calling these as functions, you’re executing the code within them immediately.


    Instead, use named exports

    export { AddController, EditController, EditOneController };
    

    and JSX elements in your routes

    import { AddController, EditController, EditOneController } from './Controller';
    
    // ...
    
    <Route path="components/addcontroller" element={<AddController />} />
    <Route path="components/editcontroller" element={<EditController />} />
    <Route path="components/editonecontroller" element={<EditOneController />} />
    
    Login or Signup to reply.
  2. People have said that the way you are exporting components is a problem, but it actually does not matter.

    export default { AddController, EditController, EditOneController }; // ok*
    export { AddController, EditController, EditOneController }; // preferred way
    

    *The only downside of exporting everything as a default export is that webpack (and other bundlers) wont be able to remove unused components.


    Your only actual issue is that doing

    <Route path="components/addcontroller" element={controller.AddController()} />
    <Route path="components/editcontroller" element={controller.EditController()} />
    <Route path="components/editonecontroller" element={controller.EditOneController()} />
    

    Is equivalent to this example with self executing functions:

    <Route
      path="components/addcontroller"
      element={(() => {/*...*/ useEffect(); /*...*/ return <div>...</div>})()}
    />
    <Route
      path="components/editcontroller"
      element={(() => {/*...*/ useEffect(); /*...*/ return <div>...</div>})()}
    />
    <Route
      path="components/editonecontroller"
      element={(() => {/*...*/ useEffect(); /*...*/ return <div>...</div>})()}
    />
    

    And if we unwrap these self executing functions we get:

    /*...*/
    useEffect();
    useEffect();
    useEffect();
    /*...*/
    <Route path="components/addcontroller" element={<div>...</div>} />
    <Route path="components/editcontroller" element={<div>...</div>} />
    <Route path="components/editonecontroller" element={<div>...</div>} />
    

    One tip that will help you avoid these mistakes is that your React function components should never be executed by you, but instead only used for creating JSX elements.

    <MyComponent/> // ok
    MyComponent() // not ok
    

    Switching to JSX your code should look like this

    const { AddController, EditController, EditOneController } = controller;
    
    <Route path="components/addcontroller" element={<AddController/>} />
    <Route path="components/editcontroller" element={<EditController/>} />
    <Route path="components/editonecontroller" element={<EditOneController/>} />
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search