Keanan Koppenhaver Keanan Koppenhaver - 20 days ago 7
React JSX Question

react-grid-layout example not working

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'))

Answer

The error you are receiving is a error about a missing prop. In a react component you basically have 2 places to keep your data, in it's parent and in your component itself. Your parent is often called props because those are properties you pass to the child (like a attribute in a 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 beign 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) which it is passing the required function as a property.

You can or 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).