ComponentwillMount is called twice
Asked Answered
L

1

6

I'm my componentWillMount() is called everytime I switch routes.

Is there other way how to handle changes in store's state?

When I use the two functions for the first time it's ok but, when I switch routes and go back and try to use them again I get this message

warning.js:45 Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the undefined component.

InventoryList.js

import React from "react";
import InventoryItem from "../components/InventoryItem";
import InventoryItemStore from "../stores/InventoryItemStore";
import { Link } from "react-router";

export default class InventoryList extends React.Component {
    constructor() {
        super();
        this.state = {
            items: InventoryItemStore.getAll(),
        }
    }

    componentWillMount() {
        InventoryItemStore.on("change", () => {
            this.setState({
                items: InventoryItemStore.getAll()
            });
        });
    }

    render(...);
}

InventoryStore.js

import { EventEmitter } from "events";

import dispatcher from "../dispatcher";

class InventoryItemStore extends EventEmitter {

    constructor() {
        super()
        this.items = [
            {
                id: 1,
                title: "first item",
                stockQuantity: 10
            },
            {
                id: 2,
                title: "second item",
                stockQuantity: 5
            }
        ];
    }

    getAll() {
        return this.items;
    }

    // Adds new item to the inventory store
    addItem( title, stockQuantity ) {
        const id = Date.now();
        this.items.push({
            id,
            title, // We don't have to do title: title because of ES6... Thx ES6
            stockQuantity
        });

        this.emit("change");
    }


    /**
     * Lower the stock quantity of certain item
     * @param  {integer} id
     * @param  {integer} stockQuantity
     */
    lowerQuantity( id, orderQuantity ) {

        this.items.map((item) => {

            if ( item.id == id ) {
                item.stockQuantity = item.stockQuantity - orderQuantity;
            }

        });

        this.emit("change");

    }


    handleActions( action ) {

        switch( action.type ) {
            case "ADD_ITEM": {
                const { title, stockQuantity } = action;
                this.addItem( title, stockQuantity );
            }
            case "LOWER_QUANTITY": {
                const { id, orderQuantity } = action;
                this.lowerQuantity( id, orderQuantity );
            }
        }

    }

}

const inventoryItemStore = new InventoryItemStore;

dispatcher.register(inventoryItemStore.handleActions.bind(inventoryItemStore));

export default inventoryItemStore;
Loud answered 7/8, 2016 at 13:48 Comment(2)
Would you try to write some codes which unbind 'change' event from InventoryItemStore at componentWillUnmount? When a react-router is changed, your components will unmounted and then remounted. If you don't unbind 'change' event from store, finally two events remain. It causes event listener be called twice.Pelt
thank you for your comment, you are right, but I don't know what code to put in componentWillUnmount to actually remove the change event listenerLoud
M
2

Your component will get unmounted every time you change route, and a new one will be mounted when you change back.

Since you're registering a eventhandler with InventoryItemStore.on but never unregistering it you're left with two components listening to change and the one that isn't mounted throws the error.

Use componentWillUnmount to un-register your component so it doesn't hang around like a ghost and haunt you when you navigate back.

See React lifecycle for more life-cycle hooks.

Moralist answered 7/8, 2016 at 14:5 Comment(6)
Oh thank you so much, I couldn't figure out what is the exact use of componentWillUnmount but now I see :)Loud
but do you think you could point out, where should I put componentWillUnmount?Loud
At InventoryList.jsPelt
I'm sorry I asked wrong. How to actually unmount the event. I placed the componentWillUnmount but I don't know how to unbind the 'change' eventListenerLoud
@JakubKohout You use removeListener, it's demonstrated in this answer.Moralist
Took some searching to find that and it might actually even be a duplicate of this question.Moralist

© 2022 - 2024 — McMap. All rights reserved.