Skip to content

Commit

Permalink
An update to React 15.4.2, PureComponents, immutability
Browse files Browse the repository at this point in the history
  • Loading branch information
jrowny committed Mar 5, 2017
1 parent 4013705 commit 7bcf111
Show file tree
Hide file tree
Showing 5 changed files with 236 additions and 213 deletions.
54 changes: 37 additions & 17 deletions demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import React from 'react';
import ReactDOM from 'react-dom';
import AbsoluteGrid from './index.js';
import Perf from 'react-addons-perf';
import createAbsoluteGrid from './index.js';
import SampleDisplay from './demo/SampleDisplay.jsx';
import * as data from './demo/sampleData.js';
import * as _ from 'lodash';
Expand All @@ -20,16 +21,22 @@ demo();

function demo() {

var sampleItems = data.screens;
var displayObject = (<SampleDisplay/>);
var render;
var zoom = 0.7;
let sampleItems = data.screens;
let render;
let zoom = 0.7;

//We set a property on each item to let the grid know not to show it
var onFilter = function(event){
var search = new RegExp(event.target.value, 'i');
sampleItems.forEach(function(item){
item.filtered = !item.name.match(search);
sampleItems = sampleItems.map(function(item){
const isMatched = !item.name.match(search);
if(!item.filtered || isMatched !== item.filtered) {
return {
...item,
filtered: isMatched
}
}
return item;
});
render();
};
Expand All @@ -39,24 +46,37 @@ function demo() {
source = _.find(sampleItems, {key: parseInt(source, 10)});
target = _.find(sampleItems, {key: parseInt(target, 10)});

var targetSort = target.sort;
const targetSort = target.sort;

//CAREFUL, For maximum performance we must maintain the array's order, but change sort
sampleItems.forEach(function(item){
sampleItems = sampleItems.map(function(item){
//Decrement sorts between positions when target is greater
if(target.sort > source.sort && (item.sort <= target.sort && item.sort > source.sort)){
item.sort --;
//Incremenet sorts between positions when source is greator
if(item.key === source.key) {
return {
...item,
sort: targetSort
}
} else if(target.sort > source.sort && (item.sort <= target.sort && item.sort > source.sort)){
return {
...item,
sort: item.sort - 1
};
//Increment sorts between positions when source is greater
}else if(item.sort >= target.sort && item.sort < source.sort){
item.sort ++;
return {
...item,
sort: item.sort + 1
};
}
return item;
});

source.sort = targetSort;
Perf.start();
render();
Perf.stop();
Perf.printWasted();
};

var onMoveDebounced = _.debounce(onMove, 80);
var onMoveDebounced = _.debounce(onMove, 40);

var unMountTest = function(){
if(ReactDOM.unmountComponentAtNode(document.getElementById('Demo'))){
Expand All @@ -67,9 +87,9 @@ function demo() {
}
};

const AbsoluteGrid = createAbsoluteGrid(SampleDisplay);
render = function(){
ReactDOM.render(<AbsoluteGrid items={sampleItems}
displayObject={displayObject}
onMove={onMoveDebounced}
dragEnabled={true}
zoom={zoom}
Expand Down
259 changes: 130 additions & 129 deletions lib/AbsoluteGrid.jsx
Original file line number Diff line number Diff line change
@@ -1,158 +1,159 @@
'use strict';

import React from 'react';
import React, { PropTypes, PureComponent, cloneElement } from 'react';
import { debounce, sortBy } from 'lodash';
import BaseStyleObject from './BaseDisplayObject.jsx';
import createDisplayObject from './BaseDisplayObject.jsx';
import LayoutManager from './LayoutManager.js';
import DragManager from './DragManager.js';

export default class AbsoluteGrid extends React.Component {
export default function createAbsoluteGrid(DisplayObject) {

const WrappedDisplayObject = createDisplayObject(DisplayObject);

return class extends PureComponent {
static defaultProps = {
items: [],
keyProp: 'key',
filterProp: 'filtered',
sortProp: 'sort',
itemWidth: 128,
itemHeight: 128,
verticalMargin: -1,
responsive: false,
dragEnabled: false,
animation: 'transform 300ms ease',
zoom: 1,
onMove: function(){}
}

constructor(props, context){
super(props, context);
this.onResize = debounce(this.onResize, 150);
this.dragManager = new DragManager(this.props.onMove, this.props.keyProp);
this.state = {
layoutWidth: 0,
dragItemId: 0
};
}
static propTypes = {
items: PropTypes.arrayOf(PropTypes.object).isRequired,
itemWidth: PropTypes.number,
itemHeight: PropTypes.number,
verticalMargin: PropTypes.number,
zoom: PropTypes.number,
responsive: PropTypes.bool,
dragEnabled: PropTypes.bool,
keyProp: PropTypes.string,
sortProp: PropTypes.string,
filterProp: PropTypes.string,
animation: PropTypes.string,
onMove: PropTypes.func
}

render() {
if(!this.state.layoutWidth || !this.props.items.length){
return <div ref={node => this.container = node}></div>;
constructor(props, context){
super(props, context);
this.onResize = debounce(this.onResize, 150);
this.dragManager = new DragManager(this.props.onMove, this.props.keyProp);
this.state = {
layoutWidth: 0,
dragItemId: 0
};
}

const options = {
itemWidth: this.props.itemWidth,
itemHeight: this.props.itemHeight,
verticalMargin: this.props.verticalMargin,
zoom: this.props.zoom
};

const layout = new LayoutManager(options, this.state.layoutWidth);

let filteredIndex = 0;
let sortedIndex = {};

/*
If we actually sorted the array, React would re-render the DOM nodes
Creating a sort index just tells us where each item should be
This also clears out filtered items from the sort order and
eliminates gaps and duplicate sorts
*/
sortBy(this.props.items, this.props.sortProp).forEach(item => {
if(!item[this.props.filterProp]){
const key = item[this.props.keyProp];
sortedIndex[key] = filteredIndex;
filteredIndex++;
render() {
if(!this.state.layoutWidth || !this.props.items.length){
return <div ref={node => this.container = node}></div>;
}
});

const itemsLength = this.props.items.length;
const gridItems = this.props.items.map(item => {
const key = item[this.props.keyProp];
const index = sortedIndex[key];
const style = layout.getStyle(index, this.props.animation, item[this.props.filterProp]);
const options = {
itemWidth: this.props.itemWidth,
itemHeight: this.props.itemHeight,
verticalMargin: this.props.verticalMargin,
zoom: this.props.zoom
};

const layout = new LayoutManager(options, this.state.layoutWidth);

let filteredIndex = 0;
let sortedIndex = {};

/*
If we actually sorted the array, React would re-render the DOM nodes
Creating a sort index just tells us where each item should be
This also clears out filtered items from the sort order and
eliminates gaps and duplicate sorts
*/
sortBy(this.props.items, this.props.sortProp).forEach(item => {
if(!item[this.props.filterProp]){
const key = item[this.props.keyProp];
sortedIndex[key] = filteredIndex;
filteredIndex++;
}
});

const itemsLength = this.props.items.length;
const gridItems = this.props.items.map(item => {
const key = item[this.props.keyProp];
const index = sortedIndex[key];
const style = layout.getStyle(index, this.props.animation, item[this.props.filterProp]);
return (
<WrappedDisplayObject
style={style}
item={item}
index={index}
key={key}
itemsLength={itemsLength}
keyProp={this.props.keyProp}
dragEnabled={this.props.dragEnabled}
dragManager={this.dragManager}
/>
);
});

const gridStyle = {
position: 'relative',
display: 'block',
height: layout.getTotalHeight(filteredIndex)
};

return (
<BaseStyleObject
style={style}
item={item}
index={index}
id={key}
key={key}
dragEnabled={this.props.dragEnabled}
dragManager={this.dragManager}
<div
style={gridStyle}
className="absoluteGrid"
ref={node => this.container = node}
>
{
React.cloneElement(this.props.displayObject, {
...this.props.displayObject.props,
item,
index,
itemsLength
})
}
</BaseStyleObject>
{gridItems}
</div>
);
});

const gridStyle = {
position: 'relative',
display: 'block',
height: layout.getTotalHeight(filteredIndex)
};

return (
<div
style={gridStyle}
className="absoluteGrid"
ref={node => this.container = node}
>
{gridItems}
</div>
);
}
}

componentDidMount() {
//If responsive, listen for resize
if(this.props.responsive){
window.addEventListener('resize', this.onResize);
componentDidMount() {
//If responsive, listen for resize
if(this.props.responsive){
window.addEventListener('resize', this.onResize);
}
this.onResize();
}
this.onResize();
}

componentWillUnmount() {
window.removeEventListener('resize', this.onResize);
}
componentWillUnmount() {
window.removeEventListener('resize', this.onResize);
}

onResize = () => {
if (window.requestAnimationFrame) {
window.requestAnimationFrame(this.getDOMWidth);
} else {
setTimeout(this.getDOMWidth, 66);
onResize = () => {
if (window.requestAnimationFrame) {
window.requestAnimationFrame(this.getDOMWidth);
} else {
setTimeout(this.getDOMWidth, 66);
}
}
}

getDOMWidth = () => {
const width = this.container && this.container.clientWidth;
getDOMWidth = () => {
const width = this.container && this.container.clientWidth;

if(this.state.layoutWidth !== width){
this.setState({layoutWidth: width});
}

if(this.state.layoutWidth !== width){
this.setState({layoutWidth: width});
}


}
}

/*
}
AbsoluteGrid.;
AbsoluteGrid.propTypes = {
items: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
displayObject: React.PropTypes.element.isRequired,
itemWidth: React.PropTypes.number,
itemHeight: React.PropTypes.number,
verticalMargin: React.PropTypes.number,
zoom: React.PropTypes.number,
responsive: React.PropTypes.bool,
dragEnabled: React.PropTypes.bool,
keyProp: React.PropTypes.string,
sortProp: React.PropTypes.string,
filterProp: React.PropTypes.string,
animation: React.PropTypes.string,
onMove: React.PropTypes.func
};

AbsoluteGrid.defaultProps = {
items: [],
keyProp: 'key',
filterProp: 'filtered',
sortProp: 'sort',
itemWidth: 128,
itemHeight: 128,
verticalMargin: -1,
responsive: false,
dragEnabled: false,
animation: 'transform 300ms ease',
zoom: 1,
onMove: function(){}
};
AbsoluteGrid.
*/
Loading

0 comments on commit 7bcf111

Please sign in to comment.