【问题标题】:React : How to stop re-rendering component or making an API calls on router change for the same routeReact:如何停止重新渲染组件或对同一路由更改路由器进行 API 调用
【发布时间】:2019-12-14 00:58:59
【问题描述】:

我需要帮助来停止在路由更改时再次进行相同的 API 调用。我试图在网络和 stackoverflow 上找到可用的解决方案,但我无法弄清楚修复此问题需要进行哪些更改。

我还尝试使用 sessionStorage 来存储一个标志,如果 API 调用被触发,那么标志设置为 true,并且在路由器更改时,我检查标志是否为 true,然后不要进行任何 API 调用。但随后它会清空文章列表。

我有一个带有路由的主要 App 组件,其中包含两个组件:(请参见下面给出的代码)

  1. 文章首页
  2. 书签首页

我面临的问题是,当我第一次点击http://localhost:3001 时,它会重定向到http://localhost:3001/articles 页面并正确呈现ArticleHome 组件并获取文章列表。然后我单击书签链接,它会呈现http://localhost:3001/bookmarks 路由和相关组件,并进行 API 调用以呈现书签列表。

但是当我使用路由器 http://localhost:3001/articles 返回文章页面时,由于每次路由更改时组件重新呈现,它再次触发 API 调用以获取文章。

谁能指导我如何在路由器更改和组件重新渲染时停止再次执行 API 调用?

如果数据已经加载,是否有更好的方法来处理同一组件的路由器更改时的 API 调用?

class App extends React.Component<any, any> {

    constructor(props: any) {
        super(props);
    }

    render() {
        return (
            <Router>
                <React.Fragment>
                    <Switch>
                        <Route exact path="/" render={() => <Redirect to="/articles" />} />
                        <Route path="/articles" render={() => <ArticleHome type="articles" />} />
                        <Route path="/bookmarks" render={() => <BookmarkHome type="bookmarks" />} />
                        <Route path="*" component={NoMatch} />
                    </Switch>
                </React.Fragment>
            </Router>
        );
    }
}

export default App;

在 ArticleHome 组件中,我有一个 API 调用,用于获取文章列表并在 BookmarkHome 组件我有另一个 API 调用来获取书签列表。

ArticleHome 组件:(BookmarkHome 组件的功能相同)

import React, { useReducer, useEffect, useState } from "react";

const ArticleHome = (props: any) => {

    const [state, setState] = useState({
        articles: [],
        reRender: true
    });

    const [newState, dispatch] = useReducer(articleReducer, state);

    useEffect(() => {


        // componentWillUnmount
        return () => {

            console.log('unmounting 1...');
            setState({
                ...state,
                reRender: false
            });
        }

    },[state.reRender]); // componentDidUpdate

    const fetchAllrecords = () => {
        fetch(url)
        .then((data: any) => {

            dispatch({ type: 'GET_ALL_RECORDS', data: data.docs })

            return data.docs;
        })
    }

return (
    <ul>
        <li>Render article list here</li>
    </ul>
)
}


// Reducer function
// ========================

function articleReducer(state: any, action: any) {
    switch (action.type) {
        case "GET_ALL_RECORDS":
            return {
                ...state,
                articles: action.data,
                reRender: false
            }
        }
        default:
            return state;
    }
}

但是当我使用路由器切换到上述任何组件时,它会再次执行 API 调用。

我在useEffect 钩子中传递了[state.reRender],告诉react 只有在添加任何新项目或数据更新时才重新渲染组件。

articleReducer 函数中我正在设置reRender: false

如果您需要这方面的更多信息,请告诉我。

谢谢, 吉涅什·拉瓦尔

【问题讨论】:

  • Raval 先生您好,Steve K 的回答有用吗?我遇到了同样的问题......看到他的回答后,我正在尝试迁移 Context.js 中的一些 api 调用
  • 嗨 @LD8,我没有机会尝试 Steve K 提供的解决方案。
  • 感谢您的回复。那么你解决了这个问题?
  • 是的,我注意到 useEffect() 方法有助于停止不必要的重新渲染该组件,当史蒂夫提到的正确使用时

标签: javascript reactjs api react-router


【解决方案1】:

如果我是你,我会从路由组件之外的我的 api 中获取,然后将其传递给路由。然后,您不必担心路由更改和任何涉及操纵路由器默认行为的编程。您可以通过以下两种方式之一执行此操作。

第一 在组件树中执行更高的 api 调用。因此,如果您要在应用程序组件中进行 api 调用,然后将其传递给 Route,您就不必担心路由的渲染。您的逻辑将超出其范围。

第二 如果您不想要一个凌乱的应用程序组件,您可以创建一个上下文组件并在那里执行您的逻辑。然后只需将您的组件包装在您创建的上下文组件中。像这样:

这是一个代码框Sandbox

您的应用组件

import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter, Route, Link, Switch } from "react-router-dom";

import First from "./First";
import Second from "./Second";
import Context from "./Context";
import "./styles.css";

function App() {
  return (
    <BrowserRouter>
      <div className="App">
        <h1>Router Rerendering Fix</h1>
        <ul>
          <li>
            <Link to="/">First Route</Link>
          </li>
          <li>
            <Link to="/second">Second Route</Link>
          </li>
        </ul>
        <Switch>
          <Context>
            <Route exact path="/" component={First} />
            <Route path="/second" component={Second} />
          </Context>
        </Switch>
      </div>
    </BrowserRouter>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

您的文章组件

import React, { useContext } from "react";
import {ApiContext} from "./Context";

const First = () => {
  const context = useContext(ApiContext);
  return (
    <div>
      <div>First Route</div>
      <ul>
        {context.map((article, index) => (
          <li key={index}>{article}</li>
        ))}
      </ul>
    </div>
  );
};

export default First;

你的上下文组件

import React, {useState, useEffect} from 'react';

export const ApiContext = React.createContext();

const Context = props => {
  const [articles, setArticles] = useState([]);

  useEffect(() => {
    console.log('rendered');
    setArticles(['Article One', 'Article Two', '...ect'])
  }, []);

  return (
    <ApiContext.Provider value={articles}>
      {props.children}
    </ApiContext.Provider>
  )
}

export default Context;

【讨论】:

  • 如果您有多个组件需要从网络获取不同类型的数据(例如文章、视频、播客等),您将如何做到这一点。您是否通过 Provider 向所有组件提供对所有这些集合的访问权限,即使它们只会 f.e.需要一个数据集?您是否有代码示例或指向如何执行此操作的示例的链接? @史蒂夫K
  • @RikvanVelzen 这成为一个优化问题。我真的需要知道您的应用程序需要如何使用这些数据。仅仅通过将数据传递给组件并不会真正受到打击,因此,如果您要立即安装将共同使用所有数据的组件,那么只需发出一个请求并获取所有数据并将其传递给提供者的值.如果不是,那么您如何确定需要哪个数据点并相应地调整您的提供商信息。如果每个组件只需要一个数据点,您可以创建多个提供程序。你的应用结构是什么?
  • @RikvanVelzen 只需为获取所需数据的每条路线发出一个请求。传递数据不会影响性能,因为您只是引用内存中的一个点。您可以像value={apiData} 一样传递它并在组件中解析或分解它value={{videos, podcasts, ect}} 没关系,我更喜欢分解它。将您的每个路由组件包装在提供程序中,在此处获取您需要的数据,并通过单个请求在所有组件中使用您需要的数据。这也将防止不必要的重新渲染,因为您已经拥有了路线所需的所有数据。
猜你喜欢
  • 2021-08-13
  • 1970-01-01
  • 2019-06-04
  • 1970-01-01
  • 2022-11-12
  • 2018-12-04
  • 1970-01-01
  • 2019-07-22
  • 2020-08-26
相关资源
最近更新 更多