Jose Cabrera Zuniga
Jose Cabrera Zuniga

Reputation: 2625

Compilation problems while learning React DND

I am starting to learn React Drag and Drop from http://react-dnd.github.io/react-dnd/docs-tutorial.html

But I am having problems understanding the tutorial. Next are all the files I created but at compilation I get:

 dragDropBundle.js:830 Warning: Failed prop type: The prop 
  `connectDropTarget` is marked as required in `BoardSquare`, but its value is `undefined`.
   in BoardSquare (created by Board)
   in Board (created by DragDropContext(Board))
   in DragDropContext(Board)

and

dragDropBundle.js:31408 Uncaught TypeError: connectDropTarget is not a function
  at BoardSquare.render (dragDropBundle.js:31408)
  at dragDropBundle.js:16370

Can someone explain me please what is wrong? I am reading this tutorial several times and hitting the wall with it.

Here it is the content of my file entry.js from where my program starts:

import React from "react";
import ReactDOM from "react-dom";
import Square from "./Square.jsx";
import Board from "./Board.jsx";
import Knight from "./Knight.jsx";

import { observe } from "./Game.jsx";
import { canMoveKnight, moveKnight } from "./Game.jsx";

const rootEl = document.getElementById("root");

observe(knightPosition =>
  ReactDOM.render(<Board knightPosition={knightPosition} />, rootEl)
);

Here it is the content of my file Board.jsx:

import React, { Component } from "react";
import { DragDropContext } from "react-dnd";
import HTML5Backend from "react-dnd-html5-backend";

import PropTypes from "prop-types";
import Square from "./Square.jsx";
import Knight from "./Knight.jsx";
import { canMoveKnight, moveKnight } from "./Game.jsx";
import { BoardSquare } from "./BoardSquare.jsx";

export class Board extends Component {
  renderPiece(x, y) {
    const [knightX, knightY] = this.props.knightPosition;
    if (x === knightX && y === knightY) {
      return <Knight />;
    }
  }

  renderSquare(i) {
    const x = i % 8;
    const y = Math.floor(i / 8);
    return (
      <div key={i} style={{ width: "12.5%", height: "12.5%" }}>
        <BoardSquare x={x} y={y}>
          {this.renderPiece(x, y)}
        </BoardSquare>
      </div>
    );
  }

  handleSquareClick(toX, toY) {
    if (canMoveKnight(toX, toY)) {
      moveKnight(toX, toY);
    }
  }

  render() {
    console.log(this.props.knightPosition);
    const squares = [];
    for (let i = 0; i < 64; i++) {
      squares.push(this.renderSquare(i));
    }

    return (
      <div
        style={{
          width: "100%",
          height: "100%",
          display: "flex",
          flexWrap: "wrap"
        }}
      >
        {squares}
      </div>
    );
  }
}

Board.propTypes = {
  knightPosition: PropTypes.arrayOf(PropTypes.number.isRequired).isRequired
};

export default DragDropContext(HTML5Backend)(Board);

Here it is the content of my file Knight.jsx:

import React, { Component } from "react";
import PropTypes from "prop-types";
import { ItemTypes } from "./Constants.jsx";
import { DragSource } from "react-dnd";

const knightSource = {
  beginDrag(props) {
    return {};
  }
};

function collect(connect, monitor) {
  return {
    connectDragSource: connect.dragSource(),
    isDragging: monitor.isDragging()
  };
}

class Knight extends Component {
  render() {
    const { connectDragSource, isDragging } = this.props;
    return connectDragSource(
      <div
        style={{
          opacity: isDragging ? 0.5 : 1,
          fontSize: 25,
          fontWeight: "bold",
          cursor: "move"
        }}
      >
        ♘
      </div>
    );
  }
}

Knight.propTypes = {
  connectDragSource: PropTypes.func.isRequired,
  isDragging: PropTypes.bool.isRequired
};

export default DragSource(ItemTypes.KNIGHT, knightSource, collect)(Knight);

Here it is the content of my file Square.jsx:

import React, { Component } from "react";
import PropTypes from "prop-types";

export default class Square extends Component {
  render() {
    const { black } = this.props;
    const fill = black ? "black" : "white";
    const stroke = black ? "white" : "black";

    return (
      <div
        style={{
          backgroundColor: fill,
          color: stroke,
          width: "100%",
          height: "100%"
        }}
      >
        {this.props.children}
      </div>
    );
  }
}

Square.propTypes = {
  black: PropTypes.bool
};

Here it is the content of my file: BoardSquare.jsx

import React, { Component } from "react";
import PropTypes from "prop-types";
import Square from "./Square.jsx";
import { canMoveKnight, moveKnight } from "./Game.jsx";
import { ItemTypes } from "./Constants.jsx";
import { DropTarget } from "react-dnd";

const squareTarget = {
  drop(props) {
    moveKnight(props.x, props.y);
  }
};

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

export class BoardSquare extends Component {
  render() {
    const { x, y, connectDropTarget, isOver } = this.props;
    const black = (x + y) % 2 === 1;

    return connectDropTarget(
      <div
        style={{
          position: "relative",
          width: "100%",
          height: "100%"
        }}
      >
        <Square black={black}>{this.props.children}</Square>
        {isOver && (
          <div
            style={{
              position: "absolute",
              top: 0,
              left: 0,
              height: "100%",
              width: "100%",
              zIndex: 1,
              opacity: 0.5,
              backgroundColor: "yellow"
            }}
          />
        )}
      </div>
    );
  }
}

BoardSquare.propTypes = {
  x: PropTypes.number.isRequired,
  y: PropTypes.number.isRequired,
  connectDropTarget: PropTypes.func.isRequired,
  isOver: PropTypes.bool.isRequired
};

export default DropTarget(ItemTypes.KNIGHT, squareTarget, collect)(BoardSquare);

Upvotes: 2

Views: 408

Answers (1)

nem035
nem035

Reputation: 35491

Your error is caused by this props type setting:

BoardSquare.propTypes = {
  // ...
  connectDropTarget: PropTypes.func.isRequired,
  //                               ^^^^^^^^^^^ property connectDropTarget is market as required
  // ...
};

Which marks connectDropTarget as a required prop in BoardSquare.

What should happen is that, because you wrap BoardSquare into a DropTarget, the DropTarget knows to set the value for connectDropTarget in the props of BoardSquare through the collect function.

However, what you are doing is using the barebones BoardSquare function, not the one wrapped in DropTarget.

// Board.jsx
import {BoardSquare} from './BoardSquare.jsx';
//     ^           ^ you are importing BoardSquare, not DropTarget(...)(BoardSquare)

This should be:

// Board.jsx
import BoardSquare from './BoardSquare.jsx';

So this means that, since you're not using the component wrapped in DropTarget, nothing is calling the collect function and thus nothing is setting the connectDropTarget prop.

To clean things up, you can probably remove the export of the barebones class from BoardSquare and only export the class wrapped in DropTarget:

// BoardSquare.jsx
export class BoardSquare extends Component {
// ^^^ remove this export statement

Upvotes: 2

Related Questions