2

I have something similar to a notes app, and want to be able to drag and drop cards from one group to another (by using react-dnd). Naturally, after a card is dropped, I want to remove it from the source group and add it to the target group. Removing works fine, but the card is not being rendered in the target group. Here is the relevant code:

App = React.createClass({
    getInitialState: function() {
        ...

        return {
            appState: appState
        }
    }


    removeCard: function(card) {
        var content = this.state.appState[card.groupId].content;
        content.splice(content.indexOf(card), 1);
        this.setState({ appState: this.state.appState });
    },

    addCard: function(card, target) {
        var content = this.state.appState[target.groupId].content;
        content.splice(content.indexOf(target) + 1, 0, card);
        this.setState({ appState: this.state.appState });
    },

    onCardDrop: function(source, target) {
        this.addCard(source, target); // didn't work
        this.removeCard(source); // worked
    },

    render: function() {
        var that = this;
        var appState = this.state.appState;

        return (
            <div>
                {_.map(appState, function(group) {
                    return (
                        <Group
                            key={group.id}
                            id={group.id}
                            group={group}
                            onCardDrop={that.onCardDrop} />
                    )
                })}
            </div>
        )
    }
});

So, the card is removed from the source group, but it never appears in the target group even though the console.log of the target group shows the card is there. Is it possible that for some reason the component is not rerendering.

The Group and Card components are rendering ul and li respectively.

1 Answer 1

1

I took some time to make a working example based on the code you provided... but it did work. No problems in the code you provided. This indicates that the problem lies elsewhere in your code.

I cannot give you a complete answer because the snippet you provided does not follow the Minimal, Complete, and Verifiable example rule. Though it is minimal, it's incomplete, and also not verifiable.

What I can do is paste the whole code that I made here and hope that it will be useful to you.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello React!</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react-dom.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
    <script src="https://npmcdn.com/[email protected]/dist/ReactDnDHTML5Backend.min.js"></script>
    <script src="https://npmcdn.com/[email protected]/dist/ReactDnD.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore.js"></script>
    <style>
        ul {
            display: inline-block;
            padding: 10px;
            width: 100px;
            border: 1px solid gray;
            vertical-align: top;
        }
        li {
            display: block;
            padding: 0;
            width: 100px;
            text-align: center;
            box-sizing: border-box;
            position: relative;
        }
        li.group {
        }
        li.card {
            height: 100px;
            border: 1px solid black;
            line-height: 100px;
            margin-top: 5px;
            font-size: 25px;
            font-weight: bold;
            cursor: move;
        }
        li > span {
            vertical-align: middle;
            display: inline-block;
        }
    </style>
  </head>
  <body>
    <div id="example"></div>
    <script type="text/babel">
    window.ItemTypes = {
        CARD: "card",
        GROUP_TITLE: "group-title"
    };
    </script>
    <script type="text/babel">
    var cardSource = {
      beginDrag: function (props) {
        return { cardId: props.id, groupId: props.groupId, card: props.card };
      }
    };
    function collect(connect, monitor) {
      return {
        connectDragSource: connect.dragSource(),
        isDragging: monitor.isDragging()
      }
    }
    var cardTarget = {
      drop: function (props, monitor) {
        var item = monitor.getItem();
        console.log(item.card)
        console.log(props.card)
        props.onCardDrop(item.card, props.card);
      },
      canDrop: function (props, monitor) {
        var item = monitor.getItem();
        return item.cardId != props.id;
      }
    };
    function collectTgt(connect, monitor) {
      return {
        connectDropTarget: connect.dropTarget(),
        isOver: monitor.isOver(),
        canDrop: monitor.canDrop()
      };
    }
    window.Card = React.createClass({
        propTypes: {
            connectDragSource: React.PropTypes.func.isRequired,
            isDragging: React.PropTypes.bool.isRequired,
            isOver: React.PropTypes.bool.isRequired,
            canDrop: React.PropTypes.bool.isRequired
        },
        renderOverlay: function (color) {
            return (
              <div style={{
                position: 'absolute',
                top: 0,
                left: 0,
                height: '100%',
                width: '100%',
                zIndex: 1,
                opacity: 0.5,
                backgroundColor: color,
              }} />
            );
        },

        render: function() {
            var connectDragSource = this.props.connectDragSource;
            var isDragging = this.props.isDragging;
            var connectDropTarget = this.props.connectDropTarget;
            var isOver = this.props.isOver;
            var canDrop = this.props.canDrop;
            return connectDropTarget(connectDragSource(
                <li className="card" style={{opacity: isDragging ? 0.5 : 1}}
                ><span>{this.props.card.name}-{this.props.card.groupId}</span>
                {isOver && !canDrop && this.renderOverlay('red')}
                {!isOver && canDrop && this.renderOverlay('yellow')}
                {isOver && canDrop && this.renderOverlay('green')}
                </li>
            ));
        }
    });
    window.Card = ReactDnD.DragSource(ItemTypes.CARD, cardSource, collect)(window.Card);
    window.Card = ReactDnD.DropTarget(ItemTypes.CARD, cardTarget, collectTgt)(window.Card);
    </script>
    <script type="text/babel">
    window.Group = React.createClass({
        render: function() {
            console.log(this.props.group)
            var that = this;
            return (
                <ul>
                    <li className="group">Group #{this.props.group.id}</li>
                    {_.map(this.props.group.content, function(card) {
                        return (
                            <Card
                                key={card.name}
                                id={card.name}
                                groupId={card.groupId}
                                card={card}
                                onCardDrop={that.props.onCardDrop}
                                />
                        )
                    })}
                </ul>
            );
        }
    });
    </script>
    <script type="text/babel">
    window.App = React.createClass({
        getInitialState: function() {
            return {
                appState: [
                    {
                        id: 0,
                        content: [
                            {
                                groupId: 0,
                                name: "C1"
                            },
                            {
                                groupId: 0,
                                name: "C2"
                            },
                            {
                                groupId: 0,
                                name: "C3"
                            },
                            {
                                groupId: 0,
                                name: "C4"
                            }
                        ]
                    },
                    {
                        id: 1,
                        content: [
                            {
                                groupId: 1,
                                name: "C5"
                            },
                            {
                                groupId: 1,
                                name: "C6"
                            },
                            {
                                groupId: 1,
                                name: "C7"
                            },
                            {
                                groupId: 1,
                                name: "C8"
                            }
                        ]
                    }
                ]
            };
        },

        removeCard: function(card) {
            var content = this.state.appState[card.groupId].content;
            content.splice(content.indexOf(card), 1);
            this.setState({ appState: this.state.appState });
        },

        addCard: function(card, target) {
            var content = this.state.appState[target.groupId].content;
            content.splice(content.indexOf(target) + 1, 0, card);
            card.groupId = target.groupId;
            this.setState({ appState: this.state.appState });
        },

        onCardDrop: function(source, target) {
            this.removeCard(source); // worked
            this.addCard(source, target); // worked
        },

        render: function() {
            var that = this;
            var appState = this.state.appState;

            return (
                <div>
                    {_.map(appState, function(group) {
                        return (
                            <Group
                                key={group.id}
                                id={group.id}
                                group={group}
                                onCardDrop={that.onCardDrop}
                                />
                        )
                    })}
                </div>
            )
        }
    });
    window.App = ReactDnD.DragDropContext(ReactDnDHTML5Backend)(window.App);
    </script>
    <script type="text/babel">
      ReactDOM.render(
        <App />,
        document.getElementById('example')
      );
    </script>
  </body>
</html>
Sign up to request clarification or add additional context in comments.

5 Comments

Hello Miguel, thank you very much for your interest and for taking the time to look at it. I figured it could not possibly be the two functions, it would make no sense that one works and the other not. Unfortunatelly I'm currently at work, so cannot take a look more closely at what you wrote. I will do that later today, and let you know if I found an error somewhere. Thank you very much again.
Hello again Miguel! So I've checked out the code you recreated, and ran a few tests only to realize that it actually behaves very similarly to my app. After moving the cards around, they also started disappearing until I was left with only two cards... Since my code was on a university's private gitlab, I uploaded it to github, so if you could take another look, I would be very grateful. Here is the link: github.com/Maputo/csm/tree/master/client/Sort . Hopefully you will be able to see what I'm missing.
Damn it... in fact there are some bugs in this code. I discovered one that happens when moving a card that originally was in group 1, then moved to group 2 and then moved again. It happens because the groupId of the card is not updated when moving from one group to the other.
Changed the sample: swapped calls removeCard / addCard; changed code of addCard to set the groupId of the card being moved. Now it works! No disappearing cards. =D
Now my code for adding cards works too, and your code sample also helped me discover a few additional bugs, I was completely unaware of! You've been very helpful, thank you!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.