React:如何停止重新渲染组件或在同一路由的路由器更改上停止 API 调用

Jig*_*val 5 javascript api reactjs react-router

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

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

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

  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;
Run Code Online (Sandbox Code Playgroud)

在 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

我已经[state.reRender]通过useEffect钩子告诉反应,仅当添加任何新项目或数据更新时才重新渲染组件。

articleReducer功能中我正在设置reRender: false.

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

谢谢,吉格内什·拉瓦尔

Ste*_*e K 4

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

首先, 您的 api 调用位于组件树的更高位置。因此,如果您在应用程序组件中执行 api 调用,然后将其传递给路线,您就不必担心路线的渲染。你的逻辑将在它之外。

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

这是一个codesandbox沙盒

您的应用程序组件

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);
Run Code Online (Sandbox Code Playgroud)

您的文章组件

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;
Run Code Online (Sandbox Code Playgroud)

您的上下文组件

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;
Run Code Online (Sandbox Code Playgroud)