从消费者 componentDidMount 更新反应上下文会导致无限重新渲染
Updating react context from consumer componentDidMount causes infinite re-renders
我正在使用 Context 在 React 中尝试一些状态管理 API;我想要实现的是,当我到达特定路线时,我从服务器加载数据,将其存储在上下文中,然后在页面本身中显示。这导致无限循环,其中对服务器的请求一遍又一遍地完成(并且永不停止)。
我正在尝试为提供者和消费者逻辑使用高阶组件:
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 导致了整个应用程序的重新渲染,但为什么呢?我哪里出错了,我该如何解决?
请求循环发生是因为 DataList
组件被重新渲染,调用 ComponentDidMount
,它在每次渲染后调用 getData()
。
如果组件的 props 或 state 发生变化,组件将呈现。
getData()
设置状态 属性 requestStatus
(这就是你的整个应用程序被重新渲染的原因)这是 DataList
的一个道具 - 导致重新DataList
.
的渲染
你不应该使用 requestStatus
作为 DataList
的支柱,因为无论如何你都是从上下文中得到的。
这可能是因为您的提供商 (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 });
}
}
好的,在尝试在沙箱中复制问题后,我意识到问题所在:我在渲染函数内的 HOC 中包装了一个父组件,如下所示:
<Route exact path="/datapage" component={requireLoggedInUser(Page)} />
这会强制 DataList
组件在每次应用程序重新呈现时被销毁 + 重新创建。
我正在使用 Context 在 React 中尝试一些状态管理 API;我想要实现的是,当我到达特定路线时,我从服务器加载数据,将其存储在上下文中,然后在页面本身中显示。这导致无限循环,其中对服务器的请求一遍又一遍地完成(并且永不停止)。
我正在尝试为提供者和消费者逻辑使用高阶组件:
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 导致了整个应用程序的重新渲染,但为什么呢?我哪里出错了,我该如何解决?
请求循环发生是因为 DataList
组件被重新渲染,调用 ComponentDidMount
,它在每次渲染后调用 getData()
。
如果组件的 props 或 state 发生变化,组件将呈现。
getData()
设置状态 属性 requestStatus
(这就是你的整个应用程序被重新渲染的原因)这是 DataList
的一个道具 - 导致重新DataList
.
你不应该使用 requestStatus
作为 DataList
的支柱,因为无论如何你都是从上下文中得到的。
这可能是因为您的提供商 (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 });
}
}
好的,在尝试在沙箱中复制问题后,我意识到问题所在:我在渲染函数内的 HOC 中包装了一个父组件,如下所示:
<Route exact path="/datapage" component={requireLoggedInUser(Page)} />
这会强制 DataList
组件在每次应用程序重新呈现时被销毁 + 重新创建。