Обновление реагирующего контекста от потребителя componentDidMount вызывает бесконечное повторное рендеринг - PullRequest
0 голосов
/ 16 января 2019

Я испытываю некоторое управление состоянием в React с использованием Context 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>
    );
  };
}

провайдер - это сам компонент приложения:

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 голосов
/ 16 января 2019

Это может быть связано с тем, что функция уровня вашего провайдера (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 });
      }
    }
0 голосов
/ 17 января 2019

Хорошо, после попытки воспроизвести проблему в песочнице, я понял, в чем проблема: я обернул родительский компонент в HOC внутри функции рендеринга, вот так:

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

, из-за которого компонент DataList уничтожался + создавался заново каждый раз, когда приложение перерисовывалось.

0 голосов
/ 16 января 2019

цикл запроса происходит потому, что компонент DataList перерисовывается, вызывая ComponentDidMount, что вызывает getData() после каждого рендеринга.

Компонент рендерится, если есть изменение в реквизитах или состоянии компонента.

getData() устанавливает свойство состояния requestStatus (именно поэтому все ваше приложение перерисовывается), которое является опорой DataList - вызывая повторную визуализацию DataList.

вы не должны использовать requestStatus в качестве опоры DataList, поскольку вы все равно получаете это из контекста.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...