从消费者 componentDidMount 更新反应上下文会导致无限重新渲染
Posted
技术标签:
【中文标题】从消费者 componentDidMount 更新反应上下文会导致无限重新渲染【英文标题】:Updating react context from consumer componentDidMount causes infinite re-renders 【发布时间】:2019-06-10 15:58:17 【问题描述】:我正在尝试使用 Context API 在 React 中进行一些状态管理;我想要实现的是,当我到达特定路线时,我从服务器加载数据,将其存储在上下文中,并将其显示在页面本身中。这导致了一个无限循环,其中对服务器的请求一遍又一遍地完成(并且永远不会停止)。
我正在尝试为提供者和消费者逻辑使用更高阶的组件:
import React, Component, createContext from 'react';
import RequestStatus from '../RequestStatus';
import getData from '../Api';
const dataCtx = createContext(
data: [],
getData: () => ,
requestStatus: RequestStatus.INACTIVE,
);
export default dataCtx;
export function dataContextProvider(WrappedComponent)
return class extends Component
constructor(props)
super(props);
this.state =
data: [],
getData: this.getData.bind(this),
requestStatus: RequestStatus.INACTIVE,
;
async getData()
this.setState( requestStatus: RequestStatus.RUNNING );
try
const data = await getData();
this.setState( data, requestStatus: RequestStatus.INACTIVE );
catch (error)
this.setState( requestStatus: RequestStatus.FAILED );
render()
return (
<dataCtx.Provider value=this.state>
<WrappedComponent ...this.props />
</dataCtx.Provider>
);
;
export function dataContextConsumer(WrappedComponent)
return function component(props)
return (
<dataCtx.Consumer>
dataContext => <WrappedComponent dataCtx=dataContext ...props />
</dataCtx.Consumer>
);
;
提供者是 App 组件本身:
import React, Fragment from 'react';
import dataContextProvider from './contexts/DataContext';
import userContextProvider from './contexts/UserContext';
import AppRoutes from './AppRoutes';
function App()
return (
<Fragment>
<main>
<AppRoutes />
</main>
</Fragment>
);
export default userContextProvider(dataContextProvider(App));
这是导致循环的消费者:
import React, Component from 'react';
import RequestStatus from './RequestStatus';
import dataContextConsumer from './contexts/DataContext';
class DataList extends Component
async componentDidMount()
const dataCtx: getData = this.props;
await getData();
render()
const dataCtx: data, requestStatus = this.props;
return (
/* display the data here */
);
export default dataContextConsumer(DataList);
我已尝试从消费者的 HOC 切换,但没有帮助:
import React, Component from 'react';
import RequestStatus from './RequestStatus';
import dataCtx from './contexts/DataContext';
class DataList extends Component
async componentDidMount()
const getData = this.context;
await getData();
render()
const data, requestStatus = this.context;
return (
/* display the data here */
);
DataList.contextType = dataCtx;
export default DataList;
DataList 只是我想要触发上下文更新的页面之一。
我猜是 Provider 导致了整个 App 的重新渲染,但是为什么呢?我哪里出错了,我该如何解决?
【问题讨论】:
【参考方案1】:好的,在尝试在沙盒中复制问题后,我意识到问题所在:我将父组件包装在渲染函数内的 HOC 中,如下所示:
<Route exact path="/datapage" component=requireLoggedInUser(Page) />
这会强制 DataList
组件在每次应用重新渲染时被销毁 + 重新创建。
【讨论】:
【参考方案2】:请求循环发生是因为DataList
组件被重新渲染,调用ComponentDidMount
,在每次渲染后调用getData()
。
如果组件的 props 或 state 发生变化,组件就会呈现。
getData()
设置 state 属性 requestStatus
(这就是你的整个应用程序被重新渲染的原因),这是 DataList
的一个属性 - 导致 DataList
的重新渲染。
你不应该使用requestStatus
作为DataList
的道具,因为无论如何你都是从上下文中得到的。
【讨论】:
我不确定你的意思,requestStatus
不是DataList
的道具,它就像data
一样是上下文的一部分。此外,根据反应文档,componentDidMount
应该仅在组件添加到 DOM 树时调用,而不是每次渲染时调用。组件是否随时离开 DOM?【参考方案3】:
这可能是因为您的提供程序 (dataContextProvider
) 级函数 getData
与您从 ../Api
导入的函数具有相同的命名空间。
然后我相信当下面的代码行const data = await getData();
在下面的代码块中运行时,它实际上调用了提供者getData
函数,从而导致了一个循环。
async getData()
this.setState( requestStatus: RequestStatus.RUNNING );
try
const data = await getData();
this.setState( data, requestStatus: RequestStatus.INACTIVE );
catch (error)
this.setState( requestStatus: RequestStatus.FAILED );
【讨论】:
我希望是这样,但似乎并非如此。即使名称不同,我仍然会得到循环。 嗯,很公平!我不确定它还能是什么。其他一切似乎都很好。我建议逐步消除一些复杂性,以便您可以缩小导致问题的范围。如果您使用项目的基本代码创建 sandbox 也会很有帮助,以便任何愿意提供帮助的人都可以通过沙箱调试问题。 关于沙盒的好主意,我会构建它并在帖子中编辑指向它的链接。也许它也有助于找到问题:)以上是关于从消费者 componentDidMount 更新反应上下文会导致无限重新渲染的主要内容,如果未能解决你的问题,请参考以下文章
“componentDidMount”生命周期方法不更新状态
如何更新 componentDidMount 中的上下文状态?
ComponentDidMount 中调用的 setState 没有更新状态? [复制]