【问题标题】:Meteor / React / React-Router /CreateContainer - can't get this.state to be set according to props when using paramsMeteor / React / React-Router /CreateContainer - 使用参数时无法根据道具设置 this.state
【发布时间】:2018-03-14 10:37:35
【问题描述】:

我将 Meteor 与 React、react-Router 和 createContainer 一起使用。当我转到链接时,我的一个组件有问题:

127.0.0.1:27017/testimonial/edit/id

似乎在构造函数中,道具在试图定义状态时仍然未定义:怎么会?无论如何发送道具时组件不应该重新渲染吗?

我尝试使用 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)

文件的重要部分如下:

应用路由器:

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)

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

【问题讨论】:

    标签: reactjs meteor


    【解决方案1】:

    您的问题是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 : ''
        }
    }
    

    【讨论】:

    • 感谢您的回答,它有效。但是,尽管使用您的解决方案,我可以删除 componentWillReceiveProps Hook,因为组件将在发送道具时重新加载,因此将在组件接收时设置初始状态道具,但它不起作用。解决方案总是结合 Initial state value + componentWillReceiveProps Hook ?根据道具设置状态没有更简单的解决方案吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-15
    • 1970-01-01
    • 1970-01-01
    • 2020-08-02
    • 2017-10-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多