reactjs 为什么在访问一个组件的链接时执行其他组件的useEffect

pu3pd22g  于 5个月前  发布在  React
关注(0)|答案(2)|浏览(50)

我有两个文件Profile.js和Controller.js如下。当我访问链接localhost/profile时,我看到两个额外的POST请求从前端发送到后端。
在进一步的调试中,我发现,当我访问localhost/profile链接时,Controller.js文件中的useEffect钩子正在执行。这是正常的还是我在这里做错了什么?

注意请不要关闭这个问题,这和useEffect被调用两次是不一样的,useEffect在同一个组件内被调用两次是一回事,这里执行的是其他组件的useEffect,这两者本质上是不同的。

最后,我展示了这些组件是如何包含在App.js文件中的。

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 };


这些组件包含在App.js文件中,如下所示。

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;

carvr3hs

carvr3hs1#

Controller.js中的这一行有问题

export default { AddController, EditController, EditOneController };

字符串
您要将多个组件导出为单个对象,这需要使用以下导入

import controllers from './Controller';


我只能假设你会用以下的用法

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


在将它们作为函数调用时,您将立即执行其中的代码。
而应使用命名导出

export { AddController, EditController, EditOneController };


和JSX元素

import { AddController, EditController, EditOneController } from './Controller';

// ...

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

vddsk6oq

vddsk6oq2#

有人说,你出口组件的方式是一个问题,但实际上并不重要。

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

字符串

  • 将所有内容导出为默认导出的唯一缺点是webpack(和其他插件)将无法删除未使用的组件。

你唯一的问题是

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


与下面的自执行函数示例等效:

<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>})()}
/>


如果我们打开这些自执行函数,我们得到:

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


一个可以帮助你避免这些错误的技巧是,你的React函数组件不应该被你执行,而应该只用于创建JSX元素。

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


切换到JSX,您的代码应该如下所示

const { AddController, EditController, EditOneController } = controller;

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

相关问题