Search code examples
reactjsreact-grid-layout

Having trouble with react-grid-layout example


Been trying to get into react and was looking at react-grid-layout when I came across a bit of a roadblock. I've pasted in the example from here essentially as is, but for some reason, when I drag an element it's not sticking. The error I'm getting in the console is:

Uncaught TypeError: this.props.onLayoutChange is not a function

I'm sure it's a simple thing that I'm missing, but this is my first React project and I would appreciate some guidance.

My code is included below:

    'use strict';
    var React = require('react');
    var _ = require('lodash');
    var ResponsiveReactGridLayout = require('react-grid-layout').Responsive;

    /**
     * This layout demonstrates how to use a grid with a dynamic number of     elements.
     */
    var AddRemoveLayout = React.createClass({
    getDefaultProps() {
      return {
        className: "layout",
        cols: {lg: 12, md: 10, sm: 6, xs: 4, xxs: 2},
        rowHeight: 100
      };
    },

    getInitialState() {
      return {
        items: [0, 1, 2, 3, 4].map(function(i, key, list) {
          return {i: i, x: i * 2, y: 0, w: 2, h: 2, add: i === list.length -    1};
       }),
      newCounter: 0
      };
    },

    createElement(el) {
      var removeStyle = {
        position: 'absolute',
        right: '2px',
        top: 0,
        cursor: 'pointer'
      };
      var i = el.add ? '+' : el.i;
      return (
        <div key={i} _grid={el}>
          {el.add ?
            <span className="add text" onClick={this.onAddItem} title="You can add an item by clicking here, too.">Add +</span>
          : <span className="text">{i}</span>}
        <span className="remove" style={removeStyle} onClick={this.onRemoveItem.bind(this, i)}>x</span>
      </div>
    );
    },

    onAddItem() {
      console.log('adding', 'n' + this.state.newCounter);
      this.setState({
      // Add a new item. It must have a unique key!
      items: this.state.items.concat({
        i: 'n' + this.state.newCounter,
        x: this.state.items.length * 2 % (this.state.cols || 12),
        y: Infinity, // puts it at the bottom
        w: 2,
        h: 2
      }),
      // Increment the counter to ensure key is always unique.
      newCounter: this.state.newCounter + 1
    });
  },

  // We're using the cols coming back from this to calculate where to add new items.
  onBreakpointChange(breakpoint, cols) {
    this.setState({
      breakpoint: breakpoint,
      cols: cols
    });
  },

  onLayoutChange(layout) {
    this.props.onLayoutChange(layout);
    this.setState({layout: layout});
  },

  onRemoveItem(i) {
    console.log('removing', i);
    this.setState({items: _.reject(this.state.items, {i: i})});
  },

  render() {
    return (
      <div>
        <button onClick={this.onAddItem}>Add Item</button>
        <ResponsiveReactGridLayout onLayoutChange={this.onLayoutChange} onBreakpointChange={this.onBreakpointChange}
            {...this.props}>
          {_.map(this.state.items, this.createElement)}
        </ResponsiveReactGridLayout>
      </div>
    );
  }
});

module.exports = AddRemoveLayout;


React.render(<AddRemoveLayout/>, document.getElementById('app'))

Solution

  • The error you are receiving is an error about a missing prop. In a react component you basically have 2 places to keep your data, in its parent and in your component itself. Your parent often has props while declaring it because those are properties you pass to the child (like an attribute in an HTML tag). Then we have the state which is data inside a component itself.

    The error you are receiving is saying that we didn't get a required prop from our parent (You can also see that inside the onLayoutChange(layout) function a call is being made to the this.props.onLayoutChange(layout) method).

    So basically we are missing a few props. In the example from GitHub there is a root file called test-hook.jsx (https://github.com/STRML/react-grid-layout/blob/master/test/test-hook.jsx). This root node has as a child ( the code you are trying to render directly ) in which it is passing the required function as a property.

    You can either use the test-hook.jsx or you can write your own root node which has a state with the layout and the required function which updates that state (see the github example on how to do that).