componentDidMount()未被调用,但仅在特定情况下被调用

Sam*_*Sam 6 javascript reactjs react-router react-router-v4 react-router-dom

只是抬头:这是一个非常奇怪的问题.我会尽力清楚地解释这个问题.这是在我的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));
Run Code Online (Sandbox Code Playgroud)

从路径中可以看出,两者ProjectProfileTaskProfile组件都需要ID.此外,两者ProjectProfileTaskProfile组件都是相当简单的父组件,它们做两件事:

  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);
Run Code Online (Sandbox Code Playgroud)

关于这一点最奇怪的部分是,如果我继续在ProjectProfile任何其他组件之间往返,并远离TaskProfile,则没有问题.我甚至可以ProjectProfile使用不同的ID,一切正常.只有当我转到带参数的另一个组件时,它才会失败.我可以这样做TaskProfile.我可以继续TaskProfile使用不同的ID,或者在没有参数的TaskProfile任何组件之间来回切换,一切正常.

只有我先去ProjectProfile,然后尝试去,TaskProfile反之亦然,错误发生.

错误只是告诉我数据ProjectProfileDesktop需要不存在.

进一步检查显示,在使用参数加载组件后,如果我使用参数转到另一个组件,我只是没有componentDidMount()在第二个组件中使用该方法.看起来该render()方法中的某些内容会导致问题并阻止其进入componentDidMount().我不确定是什么问题,因为我没有收到任何错误.我debuggerrender()方法的顶部放了一个.虽然我没有看到确切的错误,但流程告诉我某些事情肯定是错的.

还请注意我正在使用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')
);
Run Code Online (Sandbox Code Playgroud)

我很欣赏这方面的一些指示.

更新:

我正在观察的一个行为是,在用param加载一个组件后,当我用param命中第二个组件时,我render()首先在第一个组件后面点击该方法createElement,代码跳转到ReactInstrumentation.debugTool.onBeginLifeCycleTimer- 参见下面的屏幕截图.

在此输入图像描述

更新2:

在这一点上,我确信这个问题不是react-router由于我将我需要的ID硬编码到组件中以便我不依赖react-router或其他任何东西来获得它.

我还/:id从我TaskProfileProjectProfile组件的路由中删除了参数.

我仍然有同样的行为.因此,有些东西阻止了componentDidMount()方法被调用.我也试着componentDidUpdate()看看它是否被调用而且它不是.我放置componentWillUnmount()在两个组件中,当我在应用程序中的其他地方导航时,在两个组件中都componentWillUnmount()被调用,因此可以认为两个组件都可以卸载.

所以底线是render()方法中的某些东西阻止componentDidMount()或任何其他生命周期方法被调用,但这只发生在这种特殊情况下.

在这一点上,我很感激任何建议!

Sam*_*Sam 7

已经有一个多星期了,我在这个问题上一直在撞墙,它终于解决了.不可否认,我认为我理解的很多概念在这个过程中变得更加清晰.

该错误是由于我处理"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. 也许最重要的教训是:问题的原因可能比你想象的要简单得多 - 它通常是 - 并且代码中的逻辑可能是问题的原因 - 通常是!

希望这些课程能够帮助其他人快速,轻松地找到他们的解决方案!