React:如何停止重新渲染组件或对同一路由更改路由器进行 API 调用

Posted

技术标签:

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

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

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

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

    文章首页 书签首页

我面临的问题是,当我第一次点击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() 方法有助于停止不必要的重新渲染该组件,当史蒂夫提到的正确使用时 【参考方案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 没关系,我更喜欢分解它。将您的每个路由组件包装在提供程序中,在此处获取您需要的数据,并通过单个请求在所有组件中使用您需要的数据。这也将防止不必要的重新渲染,因为您已经拥有了路线所需的所有数据。

以上是关于React:如何停止重新渲染组件或对同一路由更改路由器进行 API 调用的主要内容,如果未能解决你的问题,请参考以下文章

React 功能组件在重新挂载后停止渲染状态更改

[react-router] React-Router 4怎样在路由变化时重新渲染同一个组件?

如何在 react 中的新路由更改上使用 useEffect 重新渲染变量

React redux和React路由器,链接更改不会重新渲染视图[重复]

页面重新渲染/更改后如何维护组件状态?

React-Router 在路由更改时重新安装组件