componentDidMount()未被调用,但仅在特定情况下被调用 [英] componentDidMount() is not getting called but only in a particular circumstance

查看:95
本文介绍了componentDidMount()未被调用,但仅在特定情况下被调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

只是抬头:这是一个非常奇怪的问题。我会尽力清楚地解释这个问题。这发生在我的 ReactJs 应用程序中,使用 React Router



<我有一些组件需要来自URL的某种类型的参数。我也有不依赖于参数的组件。



如果我选择需要任何参数的组件,则没有问题无论如何。



所以,我转到我的 Home ,然后账户清单,两者都不需要任何参数,我可以随意往返多次,没问题。



但是,如果我去一个使用参数的组件,然后尝试去另一个使用参数的组件,然后我得到一个错误。该错误表明组件 react-router 应该加载未正确安装,这会引发错误,告诉我子组件所需的数据丢失。我得到的错误非常简单。他们只是这样说:


this.props.something是必需的但未定义


这是我的路线 App.jsx

 从'react'导入React,{Component};来自'react-redux'的
import {connect};来自'redux'的
import {bindActionCreators};
从'react-router-dom'导入{Route,Switch,withRouter};

//在这里导入组件
//只需显示一个简洁的
import Home from'../components/home/Home';

import *作为来自'../actions/app-actions'的appActions;

class App扩展Component {

构造函数(道具){

super(道具);
}

render(){

return(

< div>
< Switch>
< Route exact path =/ membercomponent = {Home} />
< Route exact path =/ member / accountscomponent = {Accounts} />
< Route exact path =/ member / projectscomponent = {ProjectsList} />
< Route path =/ member / projects / profile /:idcomponent = {ProjectProfile} /> |
< Route exact path =/ member / taskscomponent = {TasksList} />
< Route path =/ member / tasks / profile /:idcomponent = {TaskProfile} />
< / Switch>
< / div>
);

}
}

函数mapStateToProps(state){

return {
member:state.member.memberData
}
}

函数mapDispatchToProps(dispatch){

return {

actions:bindActionCreators(appActions,dispatch)
};
}

导出默认withRouter(connect(mapStateToProps,mapDispatchToProps)(App));

从路线中可以看出, ProjectProfile TaskProfile 组件需要ID。此外, ProjectProfile TaskProfile 组件都是相当简单的父组件,可以做两件事:


  1. 进行API调用以加载 componentDidMount()事件中的数据

  2. 他们还根据用户的屏幕分辨率加载正确的子组件

这是我的 ProjectProfile 组件和 TaskProfile 与此完全相同。

  import'反应',{Component}来自'react'
import {connect}来自'react-redux';来自'redux'的
import {bindActionCreators};

//操作
import * as projectProfileActions来自'../../../actions/project-profile-actions';

//组件
从'./ProjectProfileDesktop'导入桌面;
从'./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>
);
}
}

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

函数mapDispatchToProps(dispatch){

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

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

最奇怪的是,如果我一直在之间来回走动ProjectProfile 和任何其他组件并远离 TaskProfile ,没有任何问题。我甚至可以用不同的ID点击 ProjectProfile ,一切正常。只有当我使用参数转到另一个组件时,它才会失败。我可以用 TaskProfile 做同样的事情。我可以持续点击 TaskProfile 使用不同的ID,或者在 TaskProfile 和任何 之间来回strong>一个参数,一切正常。



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



错误只是告诉我数据 ProjectProfileDesktop 要求不存在。



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



请注意我正在使用 withRouter 。我已经看到一些涉及 withRouter 的问题但是再一次,到目前为止我找不到具体的东西。



I还想提一下我还有Stripe提供程序包装我的 App 组件。不确定这是否也在这个问题中发挥作用。这是 render()在我的 index.js 中的样子:

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

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



更新:



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





更新2:



此时,我确信这个问题不是由 react-router 引起的,因为我将所需的ID硬编码到组件中,这样我就不依赖 react-router 或其他任何东西来获得它。



我还从中删除了 /:id 参数糅测试我的 TaskProfile ProjectProfile 组件。



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



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



我很欣赏这方面的任何建议!!!

解决方案

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



错误是由于我处理isLoading动画逻辑的逻辑错误 - - 是的,尽可能简单和愚蠢!!!



我真正想分享的是我为每个可能遇到类似行为的人所吸取的教训未来。


  1. 每次加载组件时都应该按 componentDidMount() 。即使你来回加载相同的组件。但是,如果首先卸载了组件,则只需按 componentDidMount()。如果组件未卸载,则在后续使用组件时不会遇到 componentDidMount()。因此,仔细查看您的逻辑,看看如果您离开它,您的组件是否已卸载。我认为查看是否发生这种情况的最简单方法是在其中添加 componentWillUnmount()方法,其中包含调试器。如果您在离开组件时遇到此方法,则意味着它正在卸载。

  2. 在此过程中进行了大量研究,以查看API调用的最佳位置获取数据并且很明显 componentDidMount()仍然是最好的选择。话虽如此,您可能需要在 componentDidMount()中添加其他逻辑,以确保不会进行任何不必要的API调用,因为您的数据可能仍在您的商店中。

  3. 在我的研究中,有些人建议最好使用 componentWillReceiveProps()方法来调用我的API来获取数据。这是不好的建议!事实上, componentWillReceiveProps() componentWillUpdate() componentWillMount()方法正在被弃用,因此我们不应该使用它们有两个原因:未来兼容性和遵循最佳实践。以下是有关他们被弃用的原因的更多信息: https: //reactjs.org/blog/2018/03/27/update-on-async-rendering.html

  4. 也许最重要的一课是这样的:原因是问题可能比你想象的要简单得多 - 它通常是 - 你的代码中的逻辑可能是问题的原因 - 它通常是!

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


Just a heads up: this is a really strange problem. I'll do my best to clearly explain the issue. This is happening in my ReactJs app using React Router.

I have some components that require some type of parameter coming from the URL. I also have components that do NOT depend on a parameter.

If I go to components that do not require any parameters, there are no problems whatsoever.

So, I go to my Home, then Account List, neither of which require any parameters, I can go back and forth as many times as I like, there's no problem.

However, if I go to a component that uses a parameter, then try to go another component that uses a parameter, I then get an error. The error indicates that the component react-router is supposed to load is NOT mounted properly which throws an error telling me that the data the child component needs is missing. The errors I'm getting are pretty simple ones. They simply say something like:

this.props.something is required but undefined

Here's my routes in 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));

As you can see from the routes, both ProjectProfile and TaskProfile components require ID's. Furthermore, both ProjectProfile and TaskProfile components are rather simple parent components that do two things:

  1. Make an API call to load data in the componentDidMount() event
  2. They also load the correct child component based on user's screen resolution

Here's my ProjectProfile component and the TaskProfile is pretty much identical to this.

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);

The strangest part about this is that if I keep going back and forth between ProjectProfile and any other component and stay away from TaskProfile, there are no problems. I can even hit ProjectProfile with different ID's and everything works fine. It all fails only when I go to another component with parameter. I can do the same with TaskProfile. I can keep hitting TaskProfile with different ID's OR go back and forth between TaskProfile and any component without a parameter and everything works fine.

Only if I go to ProjectProfile first, then try to go to TaskProfile or vice versa, the error occurs.

The error is simply telling me that the data ProjectProfileDesktop requires is not there.

Further inspection shows me that after loading a component with a parameter, if I go to another component with a parameter, I'm just NOT hitting the componentDidMount() method in the second component. Looks like something in the render() method is causing an issue and preventing it from going to componentDidMount(). I'm not sure what the issue is because I'm not getting any errors. I put a debugger at the top of the render() method. Though I'm not seeing an exact error, the flow shows me that something is definitely going wrong.

Please also notice that I'm using withRouter. I've seen some issues involving withRouter but again, I couldn't find anything concrete so far.

I also want to mention that I also have Stripe provider wrapping my App component. Not sure if this is playing a role in this problem as well. Here's what the render() looks like in my index.js:

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

I'd appreciate some pointers on this.

UPDATE:

A behavior I'm observing is that after loading a component with a param, when I hit the second component with a param, I hit the render() method first and right after the first createElement, the code jumps to ReactInstrumentation.debugTool.onBeginLifeCycleTimer -- see screen shot below.

UPDATE 2:

At this point, I'm convinced the issue is not caused by react-router because I hard-coded the ID I needed into the component so that I don't depend on react-router or anything else to get it.

I also removed /:id parameter from the routes for my TaskProfile and ProjectProfile components.

I still get the same behavior. So, something is preventing componentDidMount() method from being called. I also tried componentDidUpdate() to see if it was being called and it is NOT. I placed componentWillUnmount() in both components and as I navigate elsewhere in the app, in both components componentWillUnmount() gets called so one could argue that both components are unmounting OK.

So the bottom line is that something in the render() method is preventing componentDidMount() or any other lifecycle method from being called but this is only happening in this particular circumstance.

I'd appreciate any suggestions at this point!!!

解决方案

It's been more than a week I've been banging my head against the wall on this issue and it's finally solved. Admittedly, lots of concepts that I thought I understood became even clearer in the process.

The error was due a logical error on my part that handled "isLoading" animation logic -- yes, as simple and stupid as that!!!

What I really wanted to share here are the lessons I've learned for everyone who may experience a similar behavior in the future.

  1. You should hit componentDidMount() every time you load your component. Even if you go back and forth to load the same component. But you hit componentDidMount() ONLY IF your component was unmounted in the first place. If the component does not get unmounted, you will NOT hit componentDidMount() in your subsequent uses of the component. So look at your logic carefully to see if your component gets unmounted if you navigate away from it. I think the easiest way to see if this is happening is by adding componentWillUnmount() method with a debugger in it. If you're hitting this method when you navigate away from your component, that means it is unmounting.
  2. Did lots of research during this process to see where the best place to make API calls to fetch data and it's definitely clear that componentDidMount() is still the best place to do it. Having said that you may need additional logic in your componentDidMount() to make sure you don't make any unnecessary API calls because your data may still be in your store.
  3. During my research, some suggested that it's better to use componentWillReceiveProps() method to make my API call to fetch data. This is bad advice! As a matter of fact, componentWillReceiveProps(), componentWillUpdate() and componentWillMount() methods are being deprecated so we should not use them for two reasons: future compatibility and following best practices. Here's more info on why they're being deprecated: https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html
  4. Maybe the most important lesson is this: the cause of the problem may be much simpler than you think -- it usually is -- and the logic in your code may be the cause of the issue -- it usually is!

Hope these lessons will help others find their solutions quickly and painlessly!

这篇关于componentDidMount()未被调用,但仅在特定情况下被调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆