【问题标题】:componentDidMount not called未调用 componentDidMount
【发布时间】:2017-05-01 14:18:12
【问题描述】:

我有一个似乎没有触发componentDidMount 事件的组件。该组件是通过另一个组件使用 react-router 链接访问的父组件。

这是我的列表组件和子组件:

课程页面

import React from 'react';
import CourseList from './CourseList';
import CourseApi from '../../api/courseApi';

import {browserHistory} from 'react-router';

class CoursesPage extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.state = {
            courses: []
        };
        this.redirectToAddCoursePage = this.redirectToAddCoursePage.bind(this);
    }

    componentDidMount(){
        CourseApi.getAllCourses().then(coursesData => {
            this.setState({ courses: coursesData });
        }).catch(error => {
            throw(error);
        });
    }

    redirectToAddCoursePage() { browserHistory.push('/course'); }

    render() {
        const courses = this.state.courses;
        return (
            <div>
                <div className="page-header">
                    <h3>Courses</h3>
                </div>
                <input type="submit" value="New Course" className="btn btn-default btn-toolbar pull-right" onClick={this.redirectToAddCoursePage} />
                <div className="panel panel-default ">
                    <div className="panel-heading">
                        <span>&nbsp;</span>
                    </div>
                    <CourseList courses={courses} />
                </div>
            </div>        
        );
    }
}

export default CoursesPage;

课程列表行

import React from 'react';
import PropTypes from 'prop-types';
import CourseListRow from './CourseListRow';

const CourseList = ({courses}) => {
    return (
        <table className="table table-hover">
            <thead>
                <tr>
                    <th>Id</th>
                    <th>Title</th>
                    <th>Author</th>
                    <th>Category</th>
                    <th>Length</th>
                </tr>
            </thead>
            <tbody>
                { courses.map(course => <CourseListRow key={course.CourseId} course={course} /> )}
            </tbody>
        </table>
    );
};

CourseList.propTypes = {
    courses: PropTypes.array.isRequired
};

export default CourseList;

课程列表行

import React from 'react';
import PropTypes from 'prop-types';
import {Link} from 'react-router';

const CourseListRow = ({course}) => {
    return (
        <tr>
            <td><Link to={'/course/' + course.CourseId}>{course.CourseId}</Link></td>
            <td>{course.Title}</td>
            <td>{course.Author.FirstName + ' ' + course.Author.LastName}</td>
        </tr>
    );
};

CourseListRow.propTypes = {
    course: PropTypes.object.isRequired
};

export default CourseListRow;

我的路线

import React from 'react';
import { Route, IndexRoute } from 'react-router';
import App from './components/App';
import CoursesPage from './components/course/CoursesPage';
import ManageCoursePage from './components/course/ManageCoursePage';

export default (
    <Route path="/" components={App}>
        <IndexRoute component={HomePage} />
        <Route path="courses" component={CoursesPage} />
        <Route path="course" component={ManageCoursePage} />
        <Route path="course/:id" component={ManageCoursePage} />
    </Route>
);

以上所有组件都可以正常工作。但是,当我单击 CourseListRow 组件中的课程链接以路由到下面的组件时,课程对象的状态始终为空。我在 componentDidMount 事件中放置了一个调试器语句,它从来没有命中它,所以这个组件 CourseForm 子组件(未显示)永远不会得到课程:

import React from 'react';
import PropTypes from 'prop-types';
import CourseForm from './CourseForm';
import {authorSelectData} from '../../selectors/selectors';
import CourseApi from '../../api/courseApi';
import AuthorApi from '../../api/authorApi';

export class ManageCoursePage extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.state = {
            course: {},
            authors: [],
        };
    }

    componentDidMount() {
        let id = this.props.params.id;
        if (id) {
            CourseApi.getCourse(id).then(courseData => {
                this.setState({ course: courseData });
            }).catch(error => {
                throw(error);
            });
        }

        AuthorApi.getAllAuthors().then(authorsData => {
            this.setState({
                authors: authorSelectData(authorsData)
            });
        }).catch(error => {
            throw(error);
        });
    }

    render() {
        return (
            <CourseForm 
                course={this.state.course}
                allAuthors={this.state.authors}
            />
        );
    }
}


ManageCoursePage.contextTypes = {
    router: PropTypes.object
};

export default ManageCoursePage;

对于我的生活,我无法理解为什么 componentDidMount 没有触发和填充课程状态对象。任何帮助表示赞赏

跟进:

我将父 (ManageCoursePage) 组件的渲染方法更改为以下内容,注释掉 CourseForm 子组件:

render() {
    return (
        <h2>Hi {this.state.course.CourseId}</h2>
        /*<CourseForm 
            course={this.state.course}
            authors={this.state.authors}
            onChange={this.updateCourseState}
            onSave={this.saveCourse}
            onDelete={this.deleteCourse}
            onCancel={this.cancelChange}
            errors={this.state.errors}
            saving={this.state.saving}
            deleting={this.state.deleting}
        />*/
    );
}

这很有效,我得到了“Hi 11”。无论出于何种原因,我的子组件似乎都没有从我的父母那里收到道具。这可能与反应路由器有关,我错过了什么吗?这让我很困惑

【问题讨论】:

  • 也许这可以帮助github.com/reactjs/redux/issues/702我的猜测是您期望在路由更改时调用componentDidMount,但如果该组件已经安装它不会再次调用它
  • 所以ManageCoursePage 正在渲染,但componentDidMount 没有被解雇,对吗? 至少一次它几乎是不可能的,你 100% 确定这不是你的 API 调用的问题吗?
  • 詹姆斯 - 是的,就是这样。我在 componentDidMount 方法的顶部放置了一个调试器语句,但它没有命中它。如果我将其更改为 componentWillMount,它会到达那里但为时已晚,状态不会到达子组件。
  • 其实我撒了谎。如果我在渲染方法中硬编码一个测试“课程”对象并将其传递给 courseForm 组件,它会命中 componentDidMount。我看到在 componentDidMount 之前调用了渲染,这似乎是我的问题。在调用 componentDidMount 之前,状态课程对象中没有任何内容。
  • 呃。好的,在 componentDidMount 之前调用渲染不是我的问题,因为调用 this.setState 应该会导致重新渲染。我想我会更多地研究 React 组件的生命周期。

标签: javascript reactjs react-router


【解决方案1】:

我认为在 componentWillReceiveProps 而不是在 ComponentDidMount 中调用 getCourse 将解决您的问题。

  componentWillReceiveProps(nextProps) {
        var nextId = nextProps.params.id;
        if (nextId !== this.props.params.id) {
                  CourseApi.getCourse(nextId).then(courseData => {
                    this.setState({ course: courseData });
                }).catch(error => {
                    throw(error);
                });
        }
    }

【讨论】:

    【解决方案2】:

    好吧,我想通了。这个应用程序最初使用的是 redux,我从应用程序中删除了它。我想,因为我刚刚学习 react,所以马上加入 redux 可能会让我错过 react 的真正工作原理。

    无论如何,问题出在我的子组件中的一行代码(帖子中未显示)。在使用 redux 时出于什么原因,我必须将数字转换为字符串才能正常工作:

    value={ course.CourseId.toString() }
    

    这是错误的路线。

    未捕获的类型错误:无法读取未定义的属性“toString”

    由于 render 方法在 componentDidMount 之前运行并且课程对象属性尚未设置,因此 tostring 试图转换未定义的值。它在调用 componentDidMount 之前就爆炸了,这就是为什么我在调试时从不点击它的原因。

    我不知道我是如何误解这个错误的,但是一旦我摆脱了 toString() ,它就一切正常了。

    【讨论】:

      【解决方案3】:

      我遇到了同样的问题。 这是它是如何发生的以及我是如何解决它的。 我有包含子组件 B 的父组件 A。 我更新 A 以生成一个新的子组件 B' 而不是 B(但 B & B' 属于同一类型,内容不同)。 所以 A 会触发“componentDidMount”,B 会触发“componentDidMount”,但不会触发 B'。 我花了一点时间才明白,React 实际上重用了组件,只是改变了它的内容,而不是重新创建一个。

      我的解决方案是给 B&B' 添加一个“唯一键”

      key={`whateverMakesIt_${Unique}`}
      

      就像我的组件 B' 开始正确触发它的调用一样简单。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-12-09
        • 2017-11-30
        • 1970-01-01
        • 2018-03-09
        • 2017-02-06
        • 2016-03-31
        • 1970-01-01
        相关资源
        最近更新 更多