从使用者组件更新反应上下文DidMount导致无限重新渲染

时间:2019-01-16 14:26:07

标签: reactjs react-context

我正在尝试使用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只是我要触发上下文更新的页面之一。

我猜测提供商正在导致整个应用程序的重新渲染,但是为什么呢?我在哪里出问题了,该如何解决?

3 个答案:

答案 0 :(得分:1)

好吧,尝试在沙盒中复制问题之后,我意识到了问题所在:我将父组件包装在渲染函数内部的HOC中,如下所示:

<Route exact path="/datapage" component={requireLoggedInUser(Page)} />

这迫使DataList组件在每次重新渲染应用时都被销毁并重新创建。

答案 1 :(得分:0)

之所以发生请求循环,是因为DataList组件被重新渲染,调用了ComponentDidMount,在每次渲染之后调用了getData()

如果组件的道具或状态发生更改,则组件将呈现。

getData()设置状态属性requestStatus(这是重新渲染整个应用程序的原因),它是DataList的支持-导致{{1}的重新渲染}。

您不应该将DataList用作requestStatus的道具,因为无论如何都是从上下文中获取的。

答案 2 :(得分:0)

这可能是因为您的提供者(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 });
      }
    }