Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Thanks and ES6+ question #1

Closed
3Cbwaltz opened this issue Jan 15, 2016 · 7 comments
Closed

Thanks and ES6+ question #1

3Cbwaltz opened this issue Jan 15, 2016 · 7 comments

Comments

@3Cbwaltz
Copy link

Thanks for this nested set up with React DnD! I got your example running on localhost and have also got it working inside my application code.

I do have one question though. I was using the ES5 way of creating a react class...namely:

var Component = React.createClass({
    … 
});

And noticed that it just wouldn't work. The browser was giving me a ton of errors. It seems as though I need to cut to ES6/7 techniques cold-turkey in order for this to work. Is there a simple answer to why? I am just starting to convert my code to ES6 starting this week so I'm not too familiar.

My guess is it has something to do with the class declaration and inheritance. Or just simply being able to call the same Tree component inside of a child component and having it actually be "NEW".

@tamagokun
Copy link
Owner

Are you using the ES7 decorator syntax? @DropTarget...

Also, how do you export the module? I think that is important. Maybe paste in a full component and i'll see if I can identify any problems. It's been awhile since i've used createClass

@3Cbwaltz
Copy link
Author

Well it would be something like this... It doesn't use any ES7 decorators because I was only compiling ES5 and 6. The errors in the browser are:

Uncaught Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. Check the render method of Item.

Warning: React.createElement: type should not be null, undefined, boolean, or number. It should be a string (for DOM elements) or a ReactClass (for composite components). Check the render method of Item.

I definitely got it to work exactly how you did it with ES7, but I just want to know why it breaks in anything outside of ES7. I am using browserify.

Index.js

import { DragDropContext } from 'react-dnd'
import HTML5Backend from 'react-dnd-html5-backend'
import React, { PropTypes, Component } from 'react'
import Tree from './Tree';

var Index = React.createClass({
  getInitialState(){
    var state = {
      data: {
        tree: [
          {
            id: 1, title: 'Tatooine',
            children: [
              {id: 2, title: 'Endor', children: []},
              {id: 3, title: 'Hoth', children: []},
              {id: 4, title: 'Dagobah', children: []},
            ]
          },
          {
            id: 5, title: 'Death Star',
            children: []
          },
          {
            id: 6, title: 'Alderaan',
            children: [
              {
                id: 7, title: 'Bespin',
                children: [
                  {id: 8, title: 'Jakku', children: []}
                ]
              }
            ]
          }
        ]
      }
    };

    return state; 
  },
  moveItem(id, afterId, nodeId) {
    if (id == afterId) return

    let {tree} = this.state.data;

    const removeNode = (id, items) => {
      for (const node of items) {
        if (node.id == id) {
          items.splice(items.indexOf(node), 1)
          return
        }

        if (node.children && node.children.length) {
          removeNode(id, node.children)
        }
      }
    }

    const item = {...this.findItem(id, tree)}
    if (!item.id) {
      return
    }

    const dest = nodeId ? this.findItem(nodeId, tree).children : tree

    if (!afterId) {
      removeNode(id, tree)
      dest.push(item)
    } else {
      const index = dest.indexOf(dest.filter(v => v.id == afterId).shift())
      removeNode(id, tree)
      dest.splice(index, 0, item)
    }

    this.setState({data: tree})
  },
  findItem(id, items) {
    for (const node of items) {
      if (node.id == id) return node
      if (node.children && node.children.length) {
        const result = this.findItem(id, node.children)
        if (result) {
          return result
        }
      }
    }

    return false
  },
  render() {
    const {tree} = this.state.data;

    return (
      <div>
        <Tree
          parent={null}
          items={tree}
          move={this.moveItem.bind(this)}
          find={this.findItem.bind(this)}
        />
      </div>
    );
  }
});

module.exports = DragDropContext(HTML5Backend)(Index);

Tree.js

import React, { PropTypes } from 'react'
import { DropTarget } from 'react-dnd'
import Item from './Item'
import { ItemTypes } from './Constants';

const target = {
  drop() {},

  hover(props, monitor) {
    const {id: draggedId, parent, items} = monitor.getItem()

    if (!monitor.isOver({shallow: true})) return

    const descendantNode = props.find(props.parent, items)
    if (descendantNode) return
    if (parent == props.parent || draggedId == props.parent) return

    props.move(draggedId, props.id, props.parent)
  }
}

function collect(connect, monitor) {
  return {
    connectDropTarget: connect.dropTarget(),
    isOver: monitor.isOver()
  };
}

var Menu = React.createClass({
    propTypes: {
        items  : PropTypes.array.isRequired,
        parent : PropTypes.any,
        move   : PropTypes.func.isRequired,
        find   : PropTypes.func.isRequired
    },

  render() {
    const {connectDropTarget, items, parent, move, find} = this.props

    return connectDropTarget(
      <div style={{
        position: 'relative',
        minHeight: 10,
        paddingTop: 10,
        marginTop: -11,
        marginLeft: '2em'
      }}>
        {items.map((item, i) => {
          return <Item
            //new
            key={item.id}
            id={item.id}
            item={item}

            //props
            parent={parent}
            move={move}
            find={find}
          />
        })}
      </div>
    )
  }
});

module.exports = DropTarget(ItemTypes.ITEM, target, collect)(Tree);

Item.js

import React, { PropTypes } from 'react'
import { DragSource, DropTarget } from 'react-dnd'
import Tree from './Tree'
import { ItemTypes } from './Constants';

const source = {
  beginDrag(props) {
    return {
      id: props.id,
      parent: props.parent,
      items: props.item.children
    }
  },

  isDragging(props, monitor) {
    return props.id == monitor.getItem().id
  }
}

const target = {
  canDrop() {
    return false
  },

  hover(props, monitor) {
    const {id: draggedId} = monitor.getItem()
    const {id: overId} = props

    if (draggedId == overId || draggedId == props.parent) return
    if (!monitor.isOver({shallow: true})) return

    props.move(draggedId, overId, props.parent)
  }
}

function collectSource(connect, monitor) {
  return {
    connectDragSource: connect.dragSource(),
    connectDragPreview: connect.dragPreview(),
    isDragging: monitor.isDragging()
  }
}

function collectTarget(connect, monitor) {
  return {
    connectDropTarget: connect.dropTarget()
  }
}

var Item = React.createClass({
  propTypes: {
    id     : PropTypes.any.isRequired,
    parent : PropTypes.any,
    item   : PropTypes.object,
    move   : PropTypes.func,
    find   : PropTypes.func
  },
  render() {
    const {
      connectDropTarget, connectDragPreview, connectDragSource,
      item: {id, title, children}, parent, move, find
    } = this.props

    return connectDropTarget(connectDragPreview(
      <div>
        {connectDragSource(
          <div style={{
            background: 'white',
            border: '1px solid #ccc',
            padding: '1em',
            marginBottom: -1
          }}
          >{title}</div>
        )}
        <Tree
          parent={id}
          items={children}
          move={move}
          find={find}
        />
      </div>
    ))
  }
});

module.exports = DropTarget(ItemTypes.ITEM, target, collectTarget)(DragSource(ItemTypes.ITEM, source, collectSource)(Item));

@tamagokun
Copy link
Owner

from Tree.js

var Menu = React.createClass({
...
module.exports = DropTarget(ItemTypes.ITEM, target, collect)(Tree);

could that be why?

@3Cbwaltz
Copy link
Author

For continuity sake, I changed the code back to the original example. For this it should be

var Tree = React.createClass({
...
module.exports = DropTarget(ItemTypes.ITEM, target, collect)(Tree);

@tamagokun
Copy link
Owner

Oh damn, and I thought it would be easy.

I'll try to implement the example this way and see what I get.

@jotty-mcclure
Copy link

I made a fork for an ES6 version. @tamagokun - thanks for the awesome example! You rock.

https://github.com/jotty-mcclure/example-react-dnd-nested

Also, let me know if I can add this as an ES6 branch to your repo.

@tamagokun
Copy link
Owner

#8 should fix this once it lands 🙌

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants