Meteor / React / React-Router / CreateContainer - 使用params时,不能根据道具设置this.state

时间:2017-10-03 09:12:09

标签: reactjs meteor

我正在使用Meteor与React,react-Router和createContainer。当我转到链接时,我遇到了一个组件的问题:

127.0.0.1:27017/testimonial/edit/id

似乎在构造函数中,在尝试定义状态时,props仍未定义:怎么来的?无论如何,当道具被发送时,组件是否应该重新渲染?

我尝试使用componentWillReceiveProps(nextProps)钩子解决问题,但它也没有用。

它给出了以下错误(第14行是构造函数方法中的this.state函数):

Uncaught TypeError: Cannot read property 'author' of undefined
    at new TestimonialEditItem (TestimonialEditItem.js:14)
    at modules.js?hash=74f0dc7…:20359
    at measureLifeCyclePerf (modules.js?hash=74f0dc7…:20140)
    at ReactCompositeComponentWrapper._constructComponentWithoutOwner (modules.js?hash=74f0dc7…:20358)
    at ReactCompositeComponentWrapper._constructComponent (modules.js?hash=74f0dc7…:20344)
    at ReactCompositeComponentWrapper.mountComponent (modules.js?hash=74f0dc7…:20252)
    at Object.mountComponent (modules.js?hash=74f0dc7…:13083)
    at ReactCompositeComponentWrapper.performInitialMount (modules.js?hash=74f0dc7…:20435)
    at ReactCompositeComponentWrapper.mountComponent (modules.js?hash=74f0dc7…:20322)
    at Object.mountComponent (modules.js?hash=74f0dc7…:13083)

Exception from Tracker recompute function: Invariant Violation: Attempted to update component `TestimonialEditItem` that has already been unmounted (or failed to mount).
    at invariant (http://127.0.0.1:3000/packages/modules.js?hash=74f0dc773a707103dd7edec08a76b26338e4d966:4095:15)
    at ReactCompositeComponentWrapper.updateComponent (http://127.0.0.1:3000/packages/modules.js?hash=74f0dc773a707103dd7edec08a76b26338e4d966:20648:63)
    at ReactCompositeComponentWrapper.receiveComponent (http://127.0.0.1:3000/packages/modules.js?hash=74f0dc773a707103dd7edec08a76b26338e4d966:20611:10)
    at Object.receiveComponent (http://127.0.0.1:3000/packages/modules.js?hash=74f0dc773a707103dd7edec08a76b26338e4d966:13162:22)
    at ReactCompositeComponentWrapper._updateRenderedComponent (http://127.0.0.1:3000/packages/modules.js?hash=74f0dc773a707103dd7edec08a76b26338e4d966:20818:23)
    at ReactCompositeComponentWrapper._performComponentUpdate (http://127.0.0.1:3000/packages/modules.js?hash=74f0dc773a707103dd7edec08a76b26338e4d966:20788:10)
    at ReactCompositeComponentWrapper.updateComponent (http://127.0.0.1:3000/packages/modules.js?hash=74f0dc773a707103dd7edec08a76b26338e4d966:20709:12)
    at ReactCompositeComponentWrapper.performUpdateIfNecessary (http://127.0.0.1:3000/packages/modules.js?hash=74f0dc773a707103dd7edec08a76b26338e4d966:20625:12)
    at Object.performUpdateIfNecessary (http://127.0.0.1:3000/packages/modules.js?hash=74f0dc773a707103dd7edec08a76b26338e4d966:13194:22)
    at runBatchedUpdates (http://127.0.0.1:3000/packages/modules.js?hash=74f0dc773a707103dd7edec08a76b26338e4d966:12769:21)

文件的重要部分如下:

AppRouter:

const AppRouter = () => (
    <BrowserRouter>
        <div>
            <Header />
            <Switch>
                <Route path="/" component={Index} exact={true} />
                <Route path="/admin" render={() => (
                                                              isAdmin() ? (
                                                                <Admin />
                                                              ) : (
                                                                <Redirect to="/login"/>
                                                              )
                                                            )}/>
                <Route path="/testimonial/edit/:id" component={TestimonialEditItem} />
                <Route path="/reference/edit/:id" component={ReferenceEditItem} />
                <Route path="/team" component={Team} />
                <Route path="/references" component={References} />
            </Switch>
            <Footer />
        </div>
    </BrowserRouter>
);

TestimonialEditItem.js:

import React from 'react';
import PropTypes from 'prop-types';
import { Meteor } from 'meteor/meteor';
import moment from 'moment';
import { createContainer } from 'meteor/react-meteor-data';

import { Testimonials } from './../../api/testimonials';

export class TestimonialEditItem extends React.Component {

    constructor(props){
        super(props);
        this.state = {
            author: props.testimonial.author,
            body: props.testimonial.body,
            company: props.testimonial.company,
            position: props.testimonial.position
        }
    }

    componentWillReceiveProps(nextProps){
        this.setState(() => ({
            author: nextProps.testimonial.author,
            body: nextProps.testimonial.body,
            company: nextProps.testimonial.company,
            position: nextProps.testimonial.position
        }))
    }

    handleBodyChange(e) {
        const body = e.target.value;
        this.setState(() => ({  body }));
    }

    handleAuthorChange(e) {
        const author = e.target.value;
        this.setState(() => ({  author  }));
    }

    handleCompanyChange(e) {
        const company = e.target.value;
        this.setState(() => ({ company }));
    }

    handlePositionChange(e) {
        const position = e.target.value;
        this.setState(() => ({ position }));
    }

    onSubmit(e) {
        e.preventDefault();
        console.log(this.state.body);
        this.props.call('testimonials.update', this.props.testimonial._id, this.state.body, this.state.author, this.state.company, this.state.position);
        this.props.history.push('/admin');
    }

    renderEditForm() {
        console.log(this.props.ready);
        return(
            <div className="container editTestimonial__form">
                Author: <input onChange={this.handleAuthorChange.bind(this)} value={this.state.author} placeholder="Author" type="text"/>
                Company: <input onChange={this.handleCompanyChange.bind(this)} value={this.state.company} placeholder="Company" type="text"/>
                Position: <input onChange={this.handlePositionChange.bind(this)} value={this.state.position} placeholder="Position" type="text"/>
                Body: <textarea onChange={this.handleBodyChange.bind(this)} value={this.state.body} placeholder="Body"></textarea>
                <button className="editTestimonial__button" onClick={this.onSubmit.bind(this)}>Edit Testimonial</button>
            </div>
        )
    }

    render() {
        return (
            <div className="editTestimonial">
                { this.props.ready ? this.renderEditForm() : 'Pas de testimonial' }
            </div>
        );
    }
}

export default createContainer((props) => {
    const sub = Meteor.subscribe('testimonials');
    console.log(props.match.params.id);

    return {
        ready: sub.ready(),
        testimonial: Testimonials.findOne(props.match.params.id),
        call: Meteor.call
    }   
}, TestimonialEditItem)

非常感谢任何帮助/想法。

1 个答案:

答案 0 :(得分:2)

您的问题是,props.testimonial将是undefined,直到订阅完成。由于props.testimonialundefined,因此在尝试获取props.testimonial.author时无法读取错误。您可以做的是使用默认值设置初始状态。

实施例

constructor(props){
    super(props);
    this.state = {
        author: props.testimonial ? props.testimonial.author : '',
        body: props.testimonial ? props.testimonial.body : '',
        company: props.testimonial ? props.testimonial.company : '',
        position: props.testimonial ? props.testimonial.position : ''
    }
}