【问题标题】:React DND - Cannot add new item to state after a drag and drop eventReact DND - 拖放事件后无法将新项目添加到状态
【发布时间】:2017-08-26 01:25:51
【问题描述】:

在将此代码实现到我的图像上传器之前,我正在开发一个简单版本的 ReactDND。

每次添加图像时,都会将其添加到 state 并传递给 ReactDND,以便它可以拖动也可以放置(因此用户可以重新排列他们的图像)。

一切都很好,除了一件事。我遇到的问题是添加多个图像后,一旦我拖放和图像(工作),状态不再更新 ReactDND,我无法添加新图像。

下面是我的代码(注意我只是使用一个按钮来向状态添加额外的项目):

主要组件:

import React from 'react';

// Drag and drop stuff
import { DragDropContext } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import Container from './Container';

class ImageUploader extends React.Component {

    constructor(props) {
        super(props);

        this.state = {
            list: [],
            listCount: 1
        };

        this.onAddItem = this.onAddItem.bind(this);

    }

    onAddItem(e) {
        e.preventDefault();

        var listArray = this.state.list;
        var buildObject = {
            text: 'Jeremy' + this.state.listCount.toString(),
            age: '25',
            id: this.state.listCount
        };
        listArray.push(buildObject);

        let newListCount = this.state.listCount + 1;

        this.setState({
            list: listArray,
            listCount: newListCount
        });

        console.log(this.state.list);
    }

    render() {

        return (
            <div>
                <h1>Add to List</h1>
                <button onClick={this.onAddItem}>Add Item</button>
                <h1>The List</h1>
                <Container id={1} list={this.state.list} />
            </div>
        )
    }
}
export default DragDropContext(HTML5Backend)(ImageUploader);

容器:

import React, { Component } from 'react';
import update from 'react/lib/update';
import Card from './Card';
import { DropTarget } from 'react-dnd';

class Container extends Component {

    constructor(props) {
        super(props);
        this.state = { cards: props.list };
    }

    pushCard(card) {
        this.setState(update(this.state, {
            cards: {
                $push: [ card ]
            }
        }));
    }

    removeCard(index) {
        this.setState(update(this.state, {
            cards: {
                $splice: [
                    [index, 1]
                ]
            }
        }));
    }

    moveCard(dragIndex, hoverIndex) {
        const { cards } = this.state;
        const dragCard = cards[dragIndex];

        this.setState(update(this.state, {
            cards: {
                $splice: [
                    [dragIndex, 1],
                    [hoverIndex, 0, dragCard]
                ]
            }
        }));
    }


    render() {
        const { cards } = this.state;
        const { canDrop, isOver, connectDropTarget } = this.props;
        const isActive = canDrop && isOver;
        const style = {
            width: "200px",
            height: "404px",
            border: '1px dashed gray'
        };

        const backgroundColor = isActive ? 'lightgreen' : '#FFF';

        return connectDropTarget(
            <div className="houzes-dropbox">
                {cards.map((card, i) => {
                    return (
                        <Card
                            key={card.id}
                            index={i}
                            listId={this.props.id}
                            card={card}
                            removeCard={this.removeCard.bind(this)}
                            moveCard={this.moveCard.bind(this)} />
                    );
                })}
            </div>
        );
    }
}

const cardTarget = {
    drop(props, monitor, component ) {
        const { id } = props;
        const sourceObj = monitor.getItem();
        if ( id !== sourceObj.listId ) component.pushCard(sourceObj.card);
        return {
            listId: id
        };
    }
}

export default DropTarget("CARD", cardTarget, (connect, monitor) => ({
    connectDropTarget: connect.dropTarget(),
    isOver: monitor.isOver(),
    canDrop: monitor.canDrop()
}))(Container);

卡片:

import React, { Component } from 'react';
import { findDOMNode } from 'react-dom';
import { DragSource, DropTarget } from 'react-dnd';
import flow from 'lodash/flow';

const style = {
    border: '1px dashed grey',
    padding: '0.5rem 1rem',
    margin: '.5rem',
    backgroundColor: 'white',
    cursor: 'move'
};

class Card extends Component {

    render() {
        const { card, isDragging, connectDragSource, connectDropTarget } = this.props;
        const opacity = isDragging ? 0 : 1;

        // Background URL
        let backgroundUrl = {
            backgroundImage: "url(" + "http://localhost:4000/uploads/2017/8/a3ff91dc-2f80-42f7-951a-e9a74bf954d7-1200x800.jpeg" + ")"
        };

        console.log(card);

        return connectDragSource(connectDropTarget(


            <div className={`uploadedImageWrapper col-md-6 col-sm-12`}>
                <div className="uploadedImage">
                    <span style={backgroundUrl} />
                    {card.text}
                    {card.age}
                </div>
            </div>


        ));
    }
}

const cardSource = {

    beginDrag(props) {
        return {
            index: props.index,
            listId: props.listId,
            card: props.card
        };
    },

    endDrag(props, monitor) {
        const item = monitor.getItem();
        const dropResult = monitor.getDropResult();

        if ( dropResult && dropResult.listId !== item.listId ) {
            props.removeCard(item.index);
        }
    }
};

const cardTarget = {

    hover(props, monitor, component) {
        const dragIndex = monitor.getItem().index;
        const hoverIndex = props.index;
        const sourceListId = monitor.getItem().listId;

        // Don't replace items with themselves
        if (dragIndex === hoverIndex) {
            return;
        }

        // Determine rectangle on screen
        const hoverBoundingRect = findDOMNode(component).getBoundingClientRect();

        // Get vertical middle
        const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;

        // Determine mouse position
        const clientOffset = monitor.getClientOffset();

        // Get pixels to the top
        const hoverClientY = clientOffset.y - hoverBoundingRect.top;

        // Only perform the move when the mouse has crossed half of the items height
        // When dragging downwards, only move when the cursor is below 50%
        // When dragging upwards, only move when the cursor is above 50%

        // Dragging downwards
        if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
            return;
        }

        // Dragging upwards
        if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
            return;
        }

        // Time to actually perform the action
        if ( props.listId === sourceListId ) {
            props.moveCard(dragIndex, hoverIndex);

            // Note: we're mutating the monitor item here!
            // Generally it's better to avoid mutations,
            // but it's good here for the sake of performance
            // to avoid expensive index searches.
            monitor.getItem().index = hoverIndex;
        }
    }
};

export default flow(
    DropTarget("CARD", cardTarget, connect => ({
        connectDropTarget: connect.dropTarget()
    })),
    DragSource("CARD", cardSource, (connect, monitor) => ({
        connectDragSource: connect.dragSource(),
        isDragging: monitor.isDragging()
    }))
)(Card);

所以回顾一下,我可以将项目添加到状态,它们变得可拖放。但是在拖放一个元素后,我不能再向状态添加项目了。

关于解决方案的任何想法?我做错了什么?

感谢您浏览此内容以及任何答案。干杯。

【问题讨论】:

    标签: javascript reactjs drag-and-drop react-dnd


    【解决方案1】:

    @臭名昭著。

    我已经检查了你的代码并解决了这个问题。 当你拖放一个元素时,它会改变 Container 的状态,但不会改变 ImageUploader 的状态。

    所以我做了一个函数来通知Container的状态发生了变化。 我还将 componentWillReceiveProps() 函数插入到 Container 中,并在该函数中更新了 Container 的状态。

    问题终于解决了。

    这是更改后的代码。

    主要组件:

    import React from 'react';
    
    // Drag and drop stuff
    import {DragDropContext} from 'react-dnd';
    import HTML5Backend from 'react-dnd-html5-backend';
    import Container from './Container';
    
    class ImageUploader extends React.Component {
    
      constructor(props) {
        super(props);
    
        this.state = {
          list: [],
          listCount: 1
        };
    
        this.onAddItem = this
          .onAddItem
          .bind(this);
    
        this.listChanged = this.listChanged.bind(this);
    
      }
    
      onAddItem(e) {
        e.preventDefault();
    
        var listArray = this.state.list;
        var buildObject = {
          text: 'Jeremy' + this
            .state
            .listCount
            .toString(),
          age: '25',
          id: this.state.listCount
        };
        listArray.push(buildObject);
    
        let newListCount = this.state.listCount + 1;
    
        this.setState({list: listArray, listCount: newListCount});
      }
    
      listChanged(newList) {
        this.setState({
          list: newList
        })
      }
    
      render() {
    
        return (
          <div>
            <h1>Add to List</h1>
            <button onClick={this.onAddItem}>Add Item</button>
            <h1>The List</h1>
            <Container id={1} list={this.state.list} listChanged={this.listChanged}/>
          </div>
        )
      }
    }
    export default DragDropContext(HTML5Backend)(ImageUploader);
    

    容器:

    import React, { Component } from 'react';
    import update from 'react/lib/update';
    import Card from './Card';
    import { DropTarget } from 'react-dnd';
    
    class Container extends Component {
    
        constructor(props) {
            super(props);
                this.state = { cards: this.props.list };
        }
    
        pushCard(card) {
            this.setState(update(this.state, {
                cards: {
                    $push: [ card ]
                }
            }));
        }
    
        removeCard(index) {
            this.setState(update(this.state, {
                cards: {
                    $splice: [
                        [index, 1]
                    ]
                }
            }));
        }
    
        moveCard(dragIndex, hoverIndex) {
            const { cards } = this.state;
            const dragCard = cards[dragIndex];
    
            this.setState(update(this.state, {
                cards: {
                    $splice: [
                        [dragIndex, 1],
                        [hoverIndex, 0, dragCard]
                    ]
                }
            }));
        }
    
        componentWillReceiveProps(nextProps) {
            // You don't have to do this check first, but it can help prevent an unneeded render
            if (nextProps.list !== this.state.cards) {
                this.props.listChanged(this.state.cards);
            }
        }
    
    
        render() {
            const { cards } = this.state;
            const { canDrop, isOver, connectDropTarget } = this.props;
            const isActive = canDrop && isOver;
            const style = {
                width: "200px",
                height: "404px",
                border: '1px dashed gray'
            };
    
            const backgroundColor = isActive ? 'lightgreen' : '#FFF';
    
            return connectDropTarget(
                <div className="houzes-dropbox">
                    {cards.map((card, i) => {
                        return (
                            <Card
                                key={card.id}
                                index={i}
                                listId={this.props.id}
                                card={card}
                                removeCard={this.removeCard.bind(this)}
                                moveCard={this.moveCard.bind(this)} />
                        );
                    })}
                </div>
            );
        }
    }
    
    const cardTarget = {
        drop(props, monitor, component ) {
            const { id } = props;
            const sourceObj = monitor.getItem();
            if ( id !== sourceObj.listId ) component.pushCard(sourceObj.card);
            return {
                listId: id
            };
        }
    }
    
    export default DropTarget("CARD", cardTarget, (connect, monitor) => ({
        connectDropTarget: connect.dropTarget(),
        isOver: monitor.isOver(),
        canDrop: monitor.canDrop()
    }))(Container);
    

    如果这对你有帮助,我真的很高兴。

    感谢您阅读我的帖子。

    弗拉基米尔

    【讨论】:

    • 请尝试添加更正的代码以改进答案!
    • 不错,点赞!! ;)
    猜你喜欢
    • 2020-07-15
    • 1970-01-01
    • 2020-09-04
    • 1970-01-01
    • 2018-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-12
    相关资源
    最近更新 更多