【问题标题】:ReactJS: useEffect is not run when the url changesReactJS:url更改时不运行useEffect
【发布时间】:2020-02-18 21:56:43
【问题描述】:

我有一个 ReactJS 网站,但我还是个菜鸟。

我有 2 个组件

  • 第一个是 / 页面,必须在向 Web 服务器发出请求后呈现,当用户到达该路由时,我为此使用了 useEffect。
  • 第二个是/mostraMaterie/:id,必须在向 Web 服务器发出另一次请求后呈现。

第一个组件就像一个魅力,而第二个不触发 useEffect 功能。

当用户点击/组件上的按钮时,它会被重定向到具有相应id的/mostraMateria/:id

问题是/mostraMateria组件我还有一个useEffect函数,但是没有触发。

正如评论中所建议的,这是 JSFiddle 中的 mostraMateria 文件 mostraMaterie.js

App.js 处理用户连接

import React from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import MostraMaterie from "./Contenuti/MostraMaterie";
import "./App.css";

import Main from "./Contenuti/Main";

function App() {
  return (
    <Router>
      <Switch>
        <Route path="/mostraMateria/:id" component={MostraMaterie} />{" "}
        <Route exact path="/" component={Main} />{" "}
      </Switch>{" "}
    </Router>
  );
}

export default App;

Main.js 如果位置是/,则处理页面呈现并完美运行。

import React, { useEffect, useState } from "react";
import {  Link } from "react-router-dom";
import Indirizzi from "./Indirizzi";
import Bottone from "../Util/Bottone";
import "../App.css";
import FullNavBar from "../NavBar/FullNavBar";

function Main() {
  const [indirizzi, cambiaIndirizzi] = useState([]);

  const fetchIndirizzi = () => {
    async function prendiIndirizzi() {
      let indirizzi = await fetch(
        "https://vps.lellovitiello.tk/Riassunty/API/indirizzi.php"
      );
      let ind = await indirizzi.json();

      for (let i = 0; i < ind.length; i++) {
        async function prendiMaterie() {
          let materie = await fetch(
            `https://vps.lellovitiello.tk/Riassunty/API/materie.php?indirizzo=${ind[i].Indirizzo}`
          );
          materie = await materie.json();

          for (let materia of materie) {
            Object.defineProperty(
              materia,
              "nome",
              Object.getOwnPropertyDescriptor(materia, "Materia")
            );
            delete materia["Materia"];

            Object.defineProperty(
              materia,
              "id",
              Object.getOwnPropertyDescriptor(materia, "IDMateria")
            );
            delete materia["IDMateria"];
            delete materia["Indirizzo"];
          }

          Object.defineProperty(
            ind[i],
            "nome",
            Object.getOwnPropertyDescriptor(ind[i], "Indirizzo")
          );
          delete ind[i]["Indirizzo"];
          Object.assign(ind[i], { dati: materie });
          Object.assign(ind[i], { id: i });
        }

        await prendiMaterie();
      }
      console.log("Main.js", ind);
      cambiaIndirizzi(ind);
    }
    prendiIndirizzi();
  };
  useEffect(fetchIndirizzi, []);

  return (
    <React.Fragment>
      <FullNavBar elementi={indirizzi} />{" "}
      <Link to="/Login">
        <Bottone TestoBottone="Per caricare un riassunto" />
      </Link>{" "}
      {indirizzi.map(indirizzo => {
        console.log(indirizzo);
        return <Indirizzi dati={indirizzo} />;
      })}
    </React.Fragment>
  );
}

export default Main;

MostraMaterie.js 如果位置是/mostraMaterie/:id 并且不起作用,则处理页面呈现。

import React, { useEffect, useState } from "react";
import "../App.css";
import history from "../Util/history";
import { Link } from "react-router-dom";
import FullNavBar from "../NavBar/FullNavBar";
import Indirizzi from "./Indirizzi";
import Bottone from "../Util/Bottone";

function MostraMaterie(props) {
  const [anni, cambiaAnni] = useState([]);

  // *** this function is never called ***
  const fetchAnni = () => {
    async function prendiAnni() {
      let anniJson = await fetch(
        "https://vps.lellovitiello.tk/Riassunty/API/ottieniAnni.php"
      );
      let idMateria = props.match.params.id;
      let ann = [];
      anniJson = await anniJson.json();

      for (let anno of anniJson) {
        async function prendiAnteprime() {
          let anteprimeFetch = await fetch(
            `https://vps.lellovitiello.tk/Riassunty/API/anteprima.php?idMateria=${idMateria}&anno=${anno}`
          );

          anteprimeFetch = await anteprimeFetch.json();
          for (let anteprima of anteprimeFetch) {
            Object.defineProperty(
              anteprima,
              "nome",
              Object.getOwnPropertyDescriptor(anteprima, "Titolo")
            );

            Object.defineProperty(
              anteprima,
              "id",
              Object.getOwnPropertyDescriptor(anteprima, "ID")
            );
            delete anteprima["Titolo"];
            delete anteprima["ID"];
          }

          let annoOggetto = {
            nome: anno + "",
            anteprime: anteprimeFetch
          };
          ann.push(annoOggetto);
        }

        await prendiAnteprime();
      }
      debugger;
      cambiaAnni(ann);
    }
    console.log("Vengo eseguito");
    prendiAnni();
  };

  useEffect(fetchAnni, []); 
  console.log(history.location);

  return (
    <React.Fragment>
      <FullNavBar indirizzi={anni} />{" "}
      <Link to="/Login">
        <Bottone TestoBottone="Per caricare un riassunto" />
      </Link>{" "}
      <h1> CIao CIAO CIAO CIAOC </h1>{" "}
      {
        //*** this depends on the response of the web server ***
         anni.map(anno => {
              console.log("Ciao");
              return <Indirizzi dati={anno} />;
            })}{" "}
    </React.Fragment>
  );
}

export default MostraMaterie;

我怎样才能做到这一点?

编辑

我已经删除了 &lt;FullNavBar indirizzi={anni} /&gt;{" "} 并且它可以工作,现在我的请求已发送到服务器并触发了 ``fetchAnni``` 函数。

问题是,我需要我删除的那段代码,似乎 &lt;FullNavBar indirizzi={anni} /&gt;{" "} 在调用fetchAnni 之前渲染,即使按照Artur 的建议添加await prendiAnniasync fetchAnni 也不起作用。

我也从useEffect(fetchAnni, []); 更改为useEffect(fetchAnni, [props.location.pathname]); 那是一个字符串

【问题讨论】:

  • 你的代码很长,也许做一个简单的jsfiddle供其他人玩,这样他们可以更好地帮助你。 FWIW,我的猜测是您需要将 history.location 添加到 mostraMaterie 组件中 useEffect 的第二个参数。然后当历史对象更改时也会触发 useEffect。 (虽然,性能问题?)
  • 我尝试将 [history.location] 作为第二个参数添加到 useEffect 中,但仍然不起作用。
  • history.location 是一个对象,useEffect 第二个参数只能与原始变量数组(字符串,数字,...)一起使用,因此您应该选择在 useEffect 上使用的原始字段(喜欢props.match.params.id)
  • 一个建议,为了性能,尽量使用 useMemo 来避免重复计算。

标签: javascript reactjs


【解决方案1】:

第二次 useEffect arg [] :

首先:发送[] 作为 useEffect 的第二个参数会说“永远不要重新运行这个函数”(只有 2 次,在挂载和卸载时)。

删除它!没有:

您不想删除它,否则每次渲染都会调用您的效果并导致性能问题。

选择要跟踪的正确变量:

你必须给 React 所有会影响你的函数重新渲染的字段,在你的情况下是“当 URL 改变时”。

提示: React 每次都会测试自上次渲染后变量是否已更新,但我们也知道非原始将不起作用:

const a = {name: "Foo"};
const b = {name: "Foo"};
const res = (a === b); // False 

因此,您只能选择原始值。在您的情况下,最简单的是您的路线上的/:idprops.match.params.id

  useEffect(fetchAnni, [props.match.params.id]); 
  console.log(`Render for ID: ${props.match.params.id}`);

异步/等待

另外,你的函数 prendiAnni 被定义为 async 但你称之为:

console.log("Vengo eseguito");
prendiAnni();

代替

await prendiAnni();

【讨论】:

  • 感谢长篇大论,一有空我就试试。我在没有await 关键字的情况下调用了,因为我需要将整个函数设置为async ,并且当作为useEffect参数传递时,react会在控制台中抛出一个警告。
【解决方案2】:

将 URL 包含在 useEffect Hook 的依赖项数组中。

例子:

import React, { useEffect } from 'react';

const Component = () => {
    
    const url = window.location.pathname.split('/').pop();

    useEffect(() => {
    
        // Function will retrigger on URL change

    }, [url]);

    return(
        <div className = 'Whatever'>Whatever</div>
    );

}

export default Component;

【讨论】:

    【解决方案3】:
    import React, { useEffect } from 'react' 
    import { useHistory } from 'react-router-dom'
    
    const Root = () => {
        const history = useHistory()
        useEffect(() => {
            history.listen((location) => {
                console.log(`You changed the page to: ${location.pathname}`)
            })
        }, [history])
    }
    

    【讨论】:

    • 简单且工作正常。谢谢。
    【解决方案4】:

    看起来您将一个空数组作为第二个参数传递给 useEffect(),useEffect(fetchAnni, []);

    来自https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects

    如果你想运行一个效果并且只清理一次(在挂载和 unmount),您可以传递一个空数组 ([]) 作为第二个参数。这 告诉 React 你的效果不依赖于 props 的任何值 或状态,因此它永远不需要重新运行。

    如果您想在每个渲染上运行效果,您可以省略第二个参数。如果您希望它仅在某些道具/状态值更改时运行,您可以将它们作为数组值传递到第二个参数中。

    【讨论】:

      猜你喜欢
      • 2023-03-07
      • 1970-01-01
      • 1970-01-01
      • 2021-11-22
      • 1970-01-01
      • 2022-01-18
      • 1970-01-01
      • 2021-10-25
      • 1970-01-01
      相关资源
      最近更新 更多