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

IT技术 javascript reactjs react-router react-router-v4 react-router-dom
2021-04-02 05:31:58

请注意:这是一个非常奇怪的问题。我会尽力清楚地解释这个问题。这发生在我的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));

从路由中可以看出,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);

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

只有当我先去ProjectProfile,然后尝试去,TaskProfile反之亦然,才会出现错误。

错误只是告诉我所需的数据ProjectProfileDesktop不存在。

进一步检查表明,在加载带有参数的组件后,如果我转到带有参数的另一个组件,我只是没有点击componentDidMount()第二个组件中方法。看起来render()方法中的某些内容导致问题并阻止它转到componentDidMount(). 我不确定问题是什么,因为我没有收到任何错误。我把 adebugger放在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- 请参见下面的屏幕截图。

在此处输入图片说明

更新 2:

在这一点上,我确信这个问题不是react-router因为我将所需的 ID 硬编码到组件中,这样我就不会依赖react-router或其他任何东西来获取它。

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

我仍然得到相同的行为。所以,有些东西阻止了componentDidMount()方法被调用。我还尝试componentDidUpdate()查看它是否被调用,但不是。我放置componentWillUnmount()在这两个组件中,当我在应用程序的其他地方导航时,这两个组件componentWillUnmount()都被调用,因此人们可能会争辩说这两个组件都可以卸载。

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

在这一点上,我将不胜感激任何建议!!!

2个回答

一个多星期以来,我一直在这个问题上磕磕绊绊,终于解决了。诚然,很多我以为我理解的概念在这个过程中变得更加清晰。

该错误是由于我处理“isLoading”动画逻辑的逻辑错误造成的——是的,就这么简单和愚蠢!!!

我真正想在这里分享的是我为将来可能会遇到类似行为的每个人学到的经验教训。

  1. componentDidMount()每次加载组件时都应该点击即使你来回加载相同的组件。但是componentDidMount()只有当您的组件首先被卸载时,您才会点击如果组件没有被卸载,您将不会componentDidMount()在后续使用该组件时遇到问题因此,请仔细查看您的逻辑,看看如果您离开它,您的组件是否会被卸载。我认为查看是否发生这种情况的最简单方法是添加componentWillUnmount()带有 a 的方法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. 也许最重要的教训是:问题的原因可能比你想象的要简单得多——通常是——而你的代码中的逻辑可能是问题的原因——通常是!

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

同一个实例被卸载然后再次安装是如何发生的?我不使用任何将组件对象保存在任何地方的代码。
2021-06-13 05:31:58

ComponentDidMount 仅在安装组件时调用一次。当您使用props进行 api 调用时,我将使用 componentWillReceiveProps 并检查props,然后进行 api 调用。

如果您想检查您的流程是否使用了 componentDidMount,请使用 console.log 进行检查。

问候!

他说他componentDidMount()第一次到达,只是不要在下一个
2021-06-05 05:31:58
卢卡斯是对的。React 只会componentDidMount()在需要时调用有时会componentDidUpdate()在更新时调用但是,您不应再使用componentWillReceiveProps请参阅reactjs.org/docs/react-component.html#componentdidupdate
2021-06-08 05:31:58
componentDidMount()每次加载组件时应该调用吗?因此,如果我从组件 A 转到 B,然后再返回到 A,我不应该打到componentDidMount()组件 A 中吗?
2021-06-13 05:31:58
这并没有回答他的问题。他已经说过永远达不到componentDidMount()
2021-06-16 05:31:58
@MuriloCruz 请记住,这是两个独立的组件。componentDidMount()如果我来自使用参数的组件,我什至第一次都不会命中我确实在带参数的第一个组件上打了它,但没有在带参数的第二个组件上打它。
2021-06-20 05:31:58