this.setState не перерисовывает представление ReactJS и Ruby on Rails

У меня есть приложение rails с двумя компонентами reactJS (не родительскими/дочерними), которые в настоящее время обмениваются данными через глобальную систему Pub/Sub событий.

Однако, когда я запускаю this.setState({ items: this.props.items }), я получаю сообщение Cannot read property 'items' of undefined.

Любая помощь людей, которые могут предложить, почему я могу получить эту ошибку, будет высоко оценена.

Базовая установка у меня такая:

BasketContainer — подписан на два события

class BasketContainer extends React.Component{

constructor() {
    super()
    this.state = {
        items: [],
        subTotal: 0,
        totalPrice: 0,
        deliveryPrice: 0
    }
}
componentWillMount() {
    this.setState( {
        items: this.props.items,
    })
}
componentDidMount() {
    this.token = PubSub.subscribe('ADD_BASKET', this.handleUpdate)
    this.token = PubSub.subscribe('REMOVE_ITEM', this.handleUpdate)
    this.calculateTotals();
}

componentWillUnmount() {
PubSub.unsubscribe(this.token)
}

handleUpdate(msg, data){
    console.log(msg)
    this.setState({
        items:this.props.items // ERROR MESSAGE - Cannot read property 'items' of undefined
    })
}
.... Rest of file

ProductItem — Добавить в корзину Event Publisher

class ProductItem extends React.Component{

constructor() {
    super()

    this.state = { 
        name: '',
        price: 0,
        code: '',
        id: ''
    }
}

componentWillMount() {
    this.setState({
        name: this.props.data.name,
        price: this.props.data.price,
        code: this.props.data.code,
        id: this.props.data.id
    })
}

addtoBasket() {
    $.ajax({
        type: "POST",
        url: "/items",
        dataType: "json",
        data: {
            item: {
                name: this.state.name,
                price: this.state.price,
                code: this.state.code
            }
        },
        success: function(data) {
            PubSub.publish('ADD_BASKET', data); // THIS WORKS FINE
            console.log("success");
        },
        error: function () {
            console.log("error");
        }
    })
}

render(){
    let productName = this.props.data.name
    let productPrice = this.props.data.price
    let productCode = this.props.data.code
    let productImg = this.props.data.image_url

    return (
        <div className="col-xs-12 col-sm-4 product">
            <img src={productImg}/>
            <h3 className="text-center">{productName}</h3>
            <h5 className="text-center">£{productPrice}</h5>
            <div className="text-center">
                <button onClick={this.addtoBasket.bind(this)} className="btn btn-primary">Add to Basket</button>
            </div>
        </div>
    )
}
}

BasketItem – удалить из корзины Publisher

class BasketItem extends React.Component{

constructor(props) {
    super()

    this.state = { 
        name: '',
        price: 0,
        code: '',
        id: '',
        quantity: 1,
    }
}

componentWillMount() {
    this.setState({
        name: this.props.data.name,
        price: this.props.data.price,
        code: this.props.data.code,
        id: this.props.data.id,
    })
}

deleteItem() {
    let finalUrl = '/items/' + this.state.id;
    $.ajax({
        type: "DELETE",
        url: finalUrl,
        dataType: "json",
        success: function(data) {
            PubSub.publish('REMOVE_ITEM', data); // THIS WORKS FINE
        },
        error: function () {
            console.log("error");
        }
    })
}   

render(){
    let itemName = this.props.data.name
    let itemCode = this.props.data.code
    let itemQuantity = 1
    let itemPrice = (this.props.data.price * itemQuantity).toFixed(2)
    const itemId = this.props.data.id

    return(
        <tr>
            <td>{itemName}</td>
            <td>{itemCode}</td>
            <td>{itemQuantity}</td>
            <td><button className="btn btn-warning" onClick={this.deleteItem.bind(this)}>Remove</button></td>
            <td>£{itemPrice}</td>
        </tr>
    )
}
}

person Tom Pinchen    schedule 11.04.2016    source источник


Ответы (2)


Я думаю, что проблема в следующей строке кода

this.token = PubSub.subscribe('ADD_BASKET', this.handleUpdate)

Функция, которую вы передаете в качестве параметра, должна быть связана с this.

this.token = PubSub.subscribe('ADD_BASKET', this.handleUpdate.bind(this))

То же самое для действия REMOVE_ITEM. Тогда должно быть хорошо идти :)

person Rohit Singh Sengar    schedule 11.04.2016
comment
Спасибо за вашу помощь - это избавило от ошибки, но setState, похоже, не перерисовывает компонент, есть идеи, почему это может быть? :) - person Tom Pinchen; 11.04.2016
comment
В BasketContainer, componentWillMount() вы устанавливаете состояние с помощью this.props.items, в handleUpdate() вы делаете то же самое. Так что состояние там не меняется. - person Rohit Singh Sengar; 11.04.2016
comment
componentWillMount вызывается перед выполнением метода рендеринга. Важно отметить, что установка состояния на этом этапе не приведет к повторному рендерингу. В handleUpdate() вам нужно манипулировать this.state.items в соответствии с параметром «данные», а затем соответствующим образом установить состояние. - person Rohit Singh Sengar; 11.04.2016
comment
Эй, я использовал this.state.items.push(data); для отправки данных перед вызовом setState, который отлично работает. Что бы я сделал в случае, когда я удаляю элемент из массива элементов? Большое спасибо за вашу помощь - person Tom Pinchen; 11.04.2016
comment
Для этого вам нужно удалить эти «данные» из массива this.state.items. Я предполагаю, что параметр «данные» должен иметь какое-либо уникальное свойство, например «идентификатор» или «код». Итак, вы можете сделать что-то вроде, пусть newStateItems = this.state.items.filter((i) => i.id != data.id); this.setState({items:newStateItems}); - person Rohit Singh Sengar; 11.04.2016

Вы должны передать свойство items в BasketContainer из его родительского класса. Вот почему вы получаете ошибку Cannot read property 'items' of undefined.

Обновление: строка, в которой вы упомянули об ошибке, ошибка, которую вы получаете, связана с неправильной ссылкой на this;

попробуйте что-то вроде:

handleUpdate(msg, data){
    var self = this;
    this.setState({
        items: self.props.items // Here get props from self variable
    })
}
person Kishore Barik    schedule 11.04.2016
comment
Спасибо за вашу помощь. BasketContainer не имеет родительского класса, поэтому я использую систему событий Pub/Sub. Комментарий Рохита ниже избавился от ошибки, но теперь setState не перерисовывается. Любая идея, почему это может быть? - person Tom Pinchen; 11.04.2016