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-router] React-Router 4怎样在路由变化时重新渲染同一个组件?
如何在 react 中的新路由更改上使用 useEffect 重新渲染变量