componentDidMount () не вызывается, а только в определенных обстоятельствах - PullRequest
0 голосов
/ 13 июня 2018

Просто на голову: это действительно странная проблема.Я сделаю все возможное, чтобы четко объяснить проблему.Это происходит в моем приложении ReactJs, использующем React Router.

. У меня есть некоторые компоненты, для которых требуется какой-то тип параметра из URL.У меня также есть компоненты, которые НЕ зависят от параметра.

Если я перейду к компонентам, которые не требуют каких-либо параметров, проблем не возникает.

Итак,Я иду к своему Home, затем Account List, ни один из которых не требует каких-либо параметров, я могу ходить туда и обратно столько раз, сколько захочу, нет проблем.

Однако, если я иду ккомпонент, который использует параметр, затем попробуйте перейти к другому компоненту, который использует параметр, я затем получаю сообщение об ошибке.Ошибка указывает, что компонент react-router должен загружаться НЕ смонтирован должным образом, что выдает ошибку, сообщающую, что данные, необходимые дочернему компоненту, отсутствуют.Ошибки, которые я получаю, довольно простые.Они просто говорят что-то вроде:

this.props.something требуется, но не определено

Вот мои маршруты в App.jsx:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { Route, Switch, withRouter } from 'react-router-dom';

// Import components here
// Just showing one for brevity
import Home from '../components/home/Home';

import * as appActions from '../actions/app-actions';

class App extends Component {

   constructor(props) {

      super(props);
   }

   render() {

      return(

          <div>
              <Switch>
                 <Route exact path="/member" component={Home} />
                 <Route exact path="/member/accounts" component={Accounts} />
                 <Route exact path="/member/projects" component={ProjectsList} />
                 <Route path="/member/projects/profile/:id" component={ProjectProfile} />|
                 <Route exact path="/member/tasks" component={TasksList} />
                 <Route path="/member/tasks/profile/:id" component={TaskProfile} />
              </Switch>
          </div>
      );

   }
}

function mapStateToProps(state) {

    return {
        member: state.member.memberData
    }
}

function mapDispatchToProps(dispatch) {

    return {

        actions: bindActionCreators(appActions, dispatch)
    };
}

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(App));

Как видно из маршрутов, для компонентов ProjectProfile и TaskProfile требуются идентификаторы.Кроме того, оба компонента ProjectProfile и TaskProfile являются довольно простыми родительскими компонентами, которые выполняют две функции:

  1. Выполнение вызова API для загрузки данных в событие componentDidMount()
  2. . Онитакже загрузите правильный дочерний компонент в зависимости от разрешения экрана пользователя

Вот мой ProjectProfile компонент, и TaskProfile в значительной степени идентичен этому.

import React, { Component } from 'react'
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

// Actions
import * as projectProfileActions from '../../../actions/project-profile-actions';

// Components
import Desktop from './ProjectProfileDesktop';
import Mobile from './ProjectProfileMobile';

class ProjectProfile extends Component {

    constructor(props) {

        super(props);
    };

    componentDidMount() {

        const id = this.props.match.params.id;
        this.props.actions.getData(id);
    }

    render() { 

        return (
                <div className="height-100 width-100">
                    <div className="height-100 width-100 row row-clean">
                    {this.props.ui.isDesktop || this.props.ui.isTablet ? <Desktop />
                        : this.props.ui.isMobile ? <Mobile />
                        : null}
                    </div>
            </div>
        );
    }
}

function mapStateToProps(state) {
    return {
        ui: state.app.window
    };
}

function mapDispatchToProps(dispatch) {

    return {
        actions: bindActionCreators(projectProfileActions, dispatch)
    };
}

export default connect(mapStateToProps, mapDispatchToProps)(ProjectProfile);

Самая странная частьв том-то и дело, что если я продолжу переходить между ProjectProfile и любым другим компонентом и буду держаться подальше от TaskProfile, проблем не будет.Я даже могу нажать ProjectProfile с разными идентификаторами, и все работает отлично.Все это терпит неудачу, только когда я перехожу к другому компоненту с параметром.Я могу сделать то же самое с TaskProfile.Я могу продолжать нажимать TaskProfile с разными идентификаторами ИЛИ переходить назад и вперед между TaskProfile и любым компонентом без параметра, и все работает нормально.

Только если я перехожу к ProjectProfileсначала, затем попытайтесь перейти к TaskProfile или наоборот, возникает ошибка.

Ошибка просто говорит мне, что данные, необходимые для ProjectProfileDesktop, отсутствуют.

Дальнейшая проверка показываетмне, что после загрузки компонента с параметром, если я перехожу к другому компоненту с параметром, я просто НЕ использую метод componentDidMount() во втором компоненте.Похоже, что-то в методе render() вызывает проблему и не позволяет перейти к componentDidMount().Я не уверен, в чем проблема, потому что я не получаю никаких ошибок.Я поставил debugger на вершину render() метода.Хотя я не вижу точной ошибки, поток показывает мне, что что-то определенно идет не так.

Пожалуйста, обратите внимание, что я использую withRouter.Я видел некоторые проблемы, связанные с withRouter, но опять же, я не смог найти ничего конкретного.

Я также хочу упомянуть, что у меня также есть поставщик Stripe, обертывающий мой App компонент.Не уверен, что это играет роль в этой проблеме.Вот как выглядит render() в моем index.js:

render(
   <Provider store={store}>
        <BrowserRouter history={browserHistory}>
          <StripeProvider apiKey="my_key">
              <App />
          </StripeProvider>
       </BrowserRouter>
   </Provider>,
   document.getElementById('root')
);

Я был бы признателен за некоторые указания на это.

ОБНОВЛЕНИЕ:

Наблюдаемое мной поведение заключается в том, что после загрузки компонента с параметром, когда я ударяю по второму компоненту с параметром, я сначала запускаю метод render(), а сразу после первого createElement код переходит на ReactInstrumentation.debugTool.onBeginLifeCycleTimer - см. Снимок экрана ниже.

enter image description here

ОБНОВЛЕНИЕ 2:

На этом этапе яЯ убежден, что проблема не вызвана react-router, потому что я жестко закодировал идентификатор, который мне нужен, в компоненте, чтобы я не зависел от react-router или чего-либо еще, чтобы его получить.

Я такжеудален параметр /:id из маршрутов для моих компонентов TaskProfile и ProjectProfile.

Я продолжаю в том же духе.Итак, что-то препятствует вызову метода componentDidMount().Я также попытался componentDidUpdate() проверить, вызывается ли он, и это НЕТ.Я поместил componentWillUnmount() в обоих компонентах, и когда я перемещаюсь в другом месте приложения, в обоих компонентах вызывается componentWillUnmount(), так что можно утверждать, что оба компонента размонтированы. ОК.

Итак, суть в том, что что-то вrender() метод запрещает вызывать componentDidMount() или любой другой метод жизненного цикла, но это происходит только в данном конкретном случае.

Буду признателен за любые предложения на данный момент !!!

Ответы [ 2 ]

0 голосов
/ 19 июня 2018

Прошло уже больше недели, как я бился головой об стену по этому вопросу, и он наконец-то решен.По общему признанию, многие понятия, которые, как мне казалось, я понял, стали еще яснее в процессе.

Ошибка произошла из-за логической ошибки с моей стороны, которая обработала логику анимации isLoading - да, так же просто и глупо, как это!!!

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

  1. Вы должны нажать componentDidMount()каждый раз, когда вы загружаете свой компонент.Даже если вы идете туда-сюда, чтобы загрузить один и тот же компонент.Но вы нажали componentDidMount() ТОЛЬКО ЕСЛИ ваш компонент был отключен в первую очередь.Если компонент не отключен, вы НЕ нажмете componentDidMount() при последующем использовании компонента.Так что внимательно посмотрите на свою логику, чтобы увидеть, не размонтирован ли ваш компонент, если вы отойдете от него.Я думаю, что самый простой способ увидеть, происходит ли это, добавив метод componentWillUnmount() с debugger в нем.Если вы используете этот метод при переходе от своего компонента, это означает, что он отключается.
  2. В ходе этого процесса было проведено много исследований, чтобы выяснить, где лучше всего выполнять вызовы API для извлечения данных, и это определенноЯсно, что componentDidMount() все еще лучшее место для этого.Сказав, что вам может понадобиться дополнительная логика в вашем componentDidMount(), чтобы убедиться, что вы не делаете ненужных вызовов API, потому что ваши данные все еще могут быть в вашем магазине.
  3. В ходе моего исследования некоторые предположили, что лучшеиспользовать метод componentWillReceiveProps() для вызова API для получения данных.Это плохой совет!На самом деле методы componentWillReceiveProps(), componentWillUpdate() и componentWillMount() устарели, поэтому мы не должны использовать их по двум причинам: совместимость в будущем и следование рекомендациям.Вот дополнительная информация о том, почему они устарели: https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html
  4. Возможно, самый важный урок заключается в следующем: причина проблемы может быть гораздо проще, чем вы думаете - обычно это происходит - и логикав вашем коде может быть причина проблемы - обычно это так!

Надеюсь, что эти уроки помогут другим найти их решения быстро и безболезненно!

0 голосов
/ 15 июня 2018

ComponentDidMount вызывается только один раз, когда компонент монтируется.Поскольку вы используете реквизит для выполнения вызова API, я бы использовал componentWillReceiveProps и проверил реквизиты, а затем сделал вызов API.

Если вы хотите, чтобы вы проверяли, что componentDidMount используется вашим потоком, используйте консоль.log, чтобы проверить это.

С уважением!

...