Skip to content

Instantly share code, notes, and snippets.

@xyzdata
Created July 12, 2017 01:21
Show Gist options
  • Save xyzdata/a2a2943e8338452a8c6c8b7c254a4d79 to your computer and use it in GitHub Desktop.
Save xyzdata/a2a2943e8338452a8c6c8b7c254a4d79 to your computer and use it in GitHub Desktop.
dva-router

dva-router

https://github.com/pmg1989/dva-admin/blob/master/src/index.js

    
import dva from 'dva';
import { browserHistory } from 'dva/router';
import createLoading from 'dva-loading';
import { message } from 'antd';

import './index.html';
import './index.css';

// 3 秒
const ERROR_MSG_DURATION = 3; 

// 1. Initialize
const app = dva({
    history: browserHistory,
    onError(e) {
        message.error(e.message, ERROR_MSG_DURATION);
        console.error("app onError -- ", error);
    }
});

// 2. Plugins
app.use(createLoading());

// 3. Model
// Moved to router.js
// app.model(require('./models/users'))

// 4. Router for browserHistory
app.router(require('./router'));

// 5. Start
app.start('#root');

dva knowledge map

https://github.com/dvajs/dva-knowledgemap#通过-connect-绑定数据

https://github.com/dvajs/dva-knowledgemap#增删改

@xyzdata
Copy link
Author

xyzdata commented Jul 13, 2017

state transition & bugs

Warning: setState(...): Cannot update during an existing state transition (such as within render or another component's constructor).
Render methods should be a pure function of props and state;
constructor side-effects are an anti-pattern, but can be moved to componentWillMount.

Unknown Prop Warning

https://facebook.github.io/react/warnings/unknown-prop.html

react-dom.js:18371 Warning: setState(...): Cannot update during an existing state transition (such as within render or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to componentWillMount.

react-props-function-call-error

image

@xyzdata
Copy link
Author

xyzdata commented Jul 13, 2017

状态更新可能是异步的

// class props & this.props.onClickFunction

class PropsTest extends React.Component{
    render() {
        return (
            <div>
                <button onClick={props.onClickFunction} >
                    Add counter
                </button>
                <p>
                    this state is 
                    <span 
                        style={{color: "#0ff", fontSize: "32px"}}
                        >
                        {this.props.counter}
                    </span>
                </p>
            </div>
        );
    };
};

// function props & no need this

const Test = (props) =>{
    return (
        <div>
            <button onClick={props.onClickFunction} >
                Add `props` counter
            </button>
            <p
                style={{color: "#0ff", fontSize: "32px"}}
                >
                {props.counter}
            </p>
        </div>
    );
};

// revState, props
// https://facebook.github.io/react/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous

class App extends React.Component{
    state = {
        counter: 1
    };
    addCounter = (e) => {
        console.log(`counter`, this.state.counter);
        console.log(`e =`, e);
        this.setState(
          (prevState, props) => (
              {
                  counter: prevState.counter + 1
              }
          )
        );
        // 状态更新可能是异步的
        setTimeout(() => {
        		console.log(`counter`, this.state.counter);
        });
    };
    render(){
        // onClickFunction={this.addCounter} , 
        // 不可以在父组件上直接调用父组件自身的方法来修改state,
        // 只可以方法通过props传递给子组件,在子组件上调用修改state的方法
        return(
            <div>
                <PropsTest 
                    onClickFunction={this.addCounter}
                    counter={this.state.counter}
                    />
                <Test 
                    onClickFunction={this.addCounter}
                    counter={this.state.counter}
                    />
            </div>
        );
    }
}


export default App;

const props = {
    name: "xgqfrms",
    age: 17
};

ReactDOM.render(
    <div>
        <App {...props}/>
    </div>
    , mountNode
);

https://jscomplete.com/repl

https://facebook.github.io/react/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous

https://facebook.github.io/react/docs/state-and-lifecycle.html#using-state-correctly

@xyzdata
Copy link
Author

xyzdata commented Jul 13, 2017

https://facebook.github.io/react/docs/state-and-lifecycle.html#using-state-correctly

不可以在父组件上直接调用父组件自身的方法来修改 state,

只可以方法通过 prop 传递给子组件,在子组件上调用修改 state 的方法

Bad Demo

// props is an object!


// message.text => array of objects
class MessageList extends React.Component {
    getChildren = () => {
        //
    }
    render() {
        const children = this.props.messages.map(
            (message, index) => {
                //console.log(`message = `, message);
            		console.log(`index = `, index);
                //console.log(`typeof index = `, typeof index);
                let xindex = 'id_' + index; 
                console.log(`xindex = `, xindex);
                //console.log(`typeof xindex = `, typeof xindex);
                return(
                    <Message
                        key={index}
                        text={message.text}
                        color={message.color}
                        xyzClick={this.props.xyzClick}
                    />
                );
            }
        );
        const styles = {
        		css1: {
            		color: 'red'
            },
            css2: {
            		color: '#0f0'
            }
        }
      	return(
            <div>
                children = {children}
                <hr />
                <div>
                    BAD: <br />
                    {/* this.props = {this.props} */}
                    <div style={styles.css1}>
                        this.props.children = {this.props.color}
                    </div>
                    {/* this.props.arr = {this.props.arr} */}
                    {/* this.props.obj = {this.props.obj} */}
                    <br />
                    <p style={styles.css2}>
                        Object Error, need using map items to item
                    </p>
                </div>
            </div>
        );
    }
}

// text={message.text} => message object
class Message extends React.Component {
    render() {
    		//console.log(`this.props.text = `, this.props.text);
        //console.log(`this.props.key= `, this.props.key);
        let style = `
        		color: red;
            font-size: 23px;
        `;
        if(this.props.key === undefined){
        		//alert(`smg!`);
            console.log(`%c this.props.key= \n`, style, this.props.key);
        }
        return (
            <div>
                <hr />
                this.props.key = {this.props.key}
                <br />
                this.props.text = {this.props.text}
                <br />
                this.props.color = <span style={{backgroundColor: this.props.color}}>{this.props.color}</span>
                <br />
                <Button color={this.props.color} xyzClick={this.props.xyzClick}>
                    <span style={{color: '#fff'}}>Delete</span>
                </Button>
            </div>
        );
    }
}

// props.children === <span style={{color: '#fff'}}>Delete</span> ??? 
class Button extends React.Component {
    render() {
        return (
            <button
                style={{background: this.props.color}} 
                onClick={(e) => this.props.xyzClick(e)} 
                >
                {this.props.children}
            </button>
        );
    }
}




const text = [
    {
    text: "text 1",
    color: "red"
    },
    {
    text: "text 2",
    color: "blue"
    },
    {
    text: "text 3",
    color: "grey"
    },
    {
    text: "text 4",
    color: "green"
    },
    {
    text: "text 5",
    color: "#f0f"
    }
];
const color = "green";
const ArrayTest = [1, 2, 3];
const ObjectTest = {"key": "value"};

class App extends React.Component{
    constructor(props){
        super(props);
        this.state  = {
            showSate: false
        };
    }
    setModalVisible = (value) => {
        console.log(`showSate`, this.state.showSate);
        console.log(`value`, value);
        this.setState({
            showSate: value
        });
        console.log(`showSate`, this.state.showSate);
    };
  	XC = (e) => {
      	let m = e.toString();
      	console.log(e, m);
      	return alert(`e.toString(); =\n`, m);
  	};
  render(){
      return(
          <div>
               <div>
                   <button onClick={() => console.log(`smg`)}>
                       onClick
                   </button>
                   <button onClick={()=>this.setModalVisible(true)}>
                       showModal{this.state.showSate}
                   </button>
               </div>
               <MessageList
                   messages={text}
                   color={color}
                   arr={ArrayTest}
                   obj={ObjectTest}
                   xyzClick={this.XC}/>
          </div>
      );
    }
};



export default App;

ReactDOM.render(
    <App />,
    mountNode
);

https://jscomplete.com/repl

@xyzdata
Copy link
Author

xyzdata commented Jul 13, 2017

在父组件上直接调用父组件自身的方法来修改 state,调用的方式不对!

() => { return ???;}

https://jscomplete.com/repl

// import React, {Component} from 'react';


class Item3 extends React.Component {
    constructor(props){
        super(props);
        this.state  = {
            showSateParent: false
        };
    }
    setModalVisibleParent = (value) => {
        this.setState({
            showSateParent: value
        });
    };
    render() {
        // const showSate = this.state.showSate;
        return (
            <div>
                <h1>行为分析</h1>
                {/* 
                不可以在父组件上直接调用父组件自身的方法来修改 state,
                只可以方法通过 prop 传递给子组件,在子组件上调用修改 state 的方法
                https://facebook.github.io/react/docs/state-and-lifecycle.html#using-state-correctly
                 */}
                <button onClick={() => console.log(`smg`)}>
                   showModalParent
                </button>
                 {/* 
                 <button onClick={this.setModalVisibleParent(true)}>
                   showModalParent
                </button>
                */}
                 {/* 
                 在父组件上直接调用父组件自身的方法来修改 state,调用的方式不对!
                 () => { return ???;} 
                 */}
                <button onClick={() => this.setModalVisibleParent(true)}>
                   showModalParent
                </button> 
            </div>
        );
    }
}

export default Item3;

ReactDOM.render(
    <div>
        <Item3 />
    </div>
    , mountNode
);

@xyzdata
Copy link
Author

xyzdata commented Jul 13, 2017

在父组件上直接调用父组件自身的方法来修改 state,调用的方式不对!

  1. 可以在父组件上直接调用父组件自身的方法来修改 state,调用的方式
<button onClick={() => this.setModalVisibleParent(true)}>
    showModalParent
</button> 
  1. 也可以方法通过 prop 传递给子组件,在子组件上调用修改 state 的方法 () => {}
<button onClick={this.props.onClickFunction} >
     `this` Add counter
</button>

<button onClick={props.onClickFunction} >
      Add `props` counter
</button>

demo 1

// import React, {Component} from 'react';


class Item3 extends React.Component {
    constructor(props){
        super(props);
        this.state  = {
            showSateParent: false
        };
    }
    setModalVisibleParent = (value) => {
        this.setState({
            showSateParent: value
        });
    };
    render() {
        // const showSate = this.state.showSate;
        return (
            <div>
                <h1>行为分析</h1>
                {/* 
                可以在父组件上直接调用父组件自身的方法来修改 state,
                也可以方法通过 prop 传递给子组件,在子组件上调用修改 state 的方法
                https://facebook.github.io/react/docs/state-and-lifecycle.html#using-state-correctly
                 */}
                <button onClick={() => console.log(`smg`)}>
                   showModalParent
                </button>
                 {/* 
                 <button onClick={this.setModalVisibleParent(true)}>
                   showModalParent
                </button>
                */}
                 {/* 
                 在父组件上直接调用父组件自身的方法来修改 state,调用的方式不对!
                 () => { return ???;} 
                 */}
                <button onClick={() => this.setModalVisibleParent(true)}>
                   showModalParent
                </button> 
            </div>
        );
    }
}

export default Item3;

ReactDOM.render(
    <div>
        <Item3 />
    </div>
    , mountNode
);

props.onClickFunction & this.props.onClickFunction

props & class components & function components

https://facebook.github.io/react/warnings/unknown-prop.html

https://facebook.github.io/react/docs/components-and-props.html#functional-and-class-components

stateless components & function components

https://facebook.github.io/react/docs/context.html#referencing-context-in-stateless-functional-components

https://facebook.github.io/react/docs/react-without-jsx.html

The component can either be provided as a string, or as a subclass of React.Component, or a plain function for stateless components.

组件可以作为字符串提供,也可以作为React.Component的子类提供,也可以作为无状态组件的普通函数提供。

elements & components

https://facebook.github.io/react/docs/rendering-elements.html

const element = <h1>Hello, world</h1>;

controlled Components & Uncontrolled Components

https://facebook.github.io/react/docs/forms.html#controlled-components

https://facebook.github.io/react/docs/uncontrolled-components.html

https://facebook.github.io/react/docs/lifting-state-up.html

// class props & this.props.onClickFunction

class PropsTest extends React.Component{
    constructor(props){
        super(props);
    }
    render() {
        // need this
        return (
            <div>
                <button onClick={this.props.onClickFunction} >
                    `this` Add counter
                </button>
                <p>
                    this state is 
                    <span 
                        style={{color: "#0ff", fontSize: "32px"}}
                        >
                        {this.props.counter}
                    </span>
                </p>
            </div>
        );
    };
};

// function props & no need this

// 无状态组件,函数组件

const Test = (props) =>{
    return (
        <div>
            <button onClick={props.onClickFunction} >
                Add `props` counter
            </button>
            <p
                style={{color: "#0ff", fontSize: "32px"}}
                >
                {props.counter}
            </p>
        </div>
    );
};

// this.setState((revState, props) => {})
// https://facebook.github.io/react/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous

// 有状态组件,React.Component 子类组件

class App extends React.Component{
    state = {
        counter: 1
    };
    addCounter = (e) => {
        console.log(`counter`, this.state.counter);
        console.log(`e =`, e);
        this.setState(
          (prevState, props) => (
              {
                  counter: prevState.counter + 1
              }
          )
        );
        // 状态更新可能是异步的
        setTimeout(() => {
        		console.log(`counter`, this.state.counter);
        });
    };
    render(){
        // onClickFunction={this.addCounter} , 
        // 不可以在父组件上直接调用父组件自身的方法来修改state,
        // 只可以方法通过props传递给子组件,在子组件上调用修改state的方法
        /* 
        在父组件上直接调用父组件自身的方法来修改 state,调用的方式不对!() => { return ???;} 
        */
        return(
            <div>
                <PropsTest 
                    onClickFunction={this.addCounter}
                    counter={this.state.counter}
                    />
                <Test 
                    onClickFunction={this.addCounter}
                    counter={this.state.counter}
                    />
            </div>
        );
    }
}

// 字符串组件,变量组件, element 元素

// https://facebook.github.io/react/docs/rendering-elements.html#rendering-an-element-into-the-dom

const element = <h1>Hello, world</h1>;


export default App;

const props = {
    name: "xgqfrms",
    age: 17
};

ReactDOM.render(
    <div>
        <App {...props}/>
    </div>
    , mountNode
);

@xyzdata
Copy link
Author

xyzdata commented Jul 13, 2017

@xyzdata
Copy link
Author

xyzdata commented Jul 13, 2017

@xyzdata
Copy link
Author

xyzdata commented Jul 13, 2017

卡片式页签 Tab Pane

Tab Page 标签页

https://ant.design/components/tabs-cn/

@xyzdata
Copy link
Author

xyzdata commented Jul 13, 2017

DropOption

// Warning: Failed prop type: The prop `menuOptions` is marked as required in `DropOption`, but its value is `undefined`.

import {DropOption} from '../utils/DropOption';



    {
        title: '编辑',
        dataIndex: 'edit',
        key: 'editRole',
        render: (text, record, index) => {
            const clickOK = (record) => {
                return record.pid = "new pid";
            };
            const menu = (
                <Menu>
                    <Menu.Item>
                    <a rel="noopener noreferrer" href="https://www.xgqfrms.xyz/">1st</a>
                    </Menu.Item>
                    <Menu.Item>
                    <a rel="noopener noreferrer" href="https://www.xgqfrms.xyz/">2nd</a>
                    </Menu.Item>
                    <Menu.Item>
                    <a rel="noopener noreferrer" href="https://www.xgqfrms.xyz/">3rd</a>
                    </Menu.Item>
                </Menu>
            );
            return(
                <span>
                    <DropOption 
                        onMenuClick={e => handleMenuClick(record, e)} 
                        overlay={menu}
                        menuOptions={[
                            {key: '1', name: '更新'},
                            {key: '2', name: '删除'}
                        ]} 
                    />
                    <EMC
                        data={record}
                        clickOK={clickOK}
                    >
                        <span>{text} 编辑</span>
                    </EMC>
                </span>
            );
        }
    }

@xyzdata
Copy link
Author

xyzdata commented Jul 13, 2017

二次封装 Dropdown

import React from 'react';
import PropTypes from 'prop-types';

import {Dropdown, Button, Icon, Menu} from 'antd';


// 二次封装 Dropdown

const DropOption = ({onMenuClick, menuOptions = [], buttonStyle, dropdownProps}) => {
    // 列出 menu 所有的 items
    const menuItems = menuOptions.map(
        (item) => {
            // console.log(`item `, item);
            // console.log(`item.key `, item.key);
            // console.log(`typeof item.key `, typeof(item.key));
            // if(item.key === "2" || item.name === "删除")
            const styles = {
                css1: {
                    color: "red", 
                    border: '1px solid red', 
                    borderRadius: '5px', 
                    textAlign: 'center'
                },
                css2: {
                    color: "#000", 
                    border: '1px solid #ccc', 
                    borderRadius: '5px', 
                    textAlign: 'center'
                }
            };
            if(item.name === "删除"){
                return(
                    <Menu.Item key={item.key} style={styles.css1}>
                        {item.name}
                    </Menu.Item>
                )
            }else{
                return(
                    <Menu.Item key={item.key} style={styles.css2}>
                        {item.name}
                    </Menu.Item>
                )
            }
        }
    );
    // Dropdown
    return (
        <Dropdown
            overlay={
                <Menu onClick={onMenuClick}>
                    {menuItems}
                </Menu>
            }
            trigger={['click']}
            {...dropdownProps}
            >
            <Button
                style={
                    {
                        border: 'none', 
                        background: '#ccc',
                        ...buttonStyle
                    }
                }
                >
                <Icon 
                    style={{marginRight: 5}} 
                    type="bars"
                />
                <Icon type="down" />
            </Button>
        </Dropdown>
    );
};

DropOption.propTypes = {
    onMenuClick: PropTypes.func,
    menuOptions: PropTypes.array.isRequired,
    buttonStyle: PropTypes.object,
    dropdownProps: PropTypes.object
};

export {DropOption};
export default DropOption;

@xyzdata
Copy link
Author

xyzdata commented Jul 13, 2017

date.toString().substr(0,10)

        {
            title: '创建时间',
            dataIndex: 'createTime',
            key: 'createTime',
            render: (date) => (
                // 2017-02-12 08:11:08
                // substr(begin [,end])
                // substr(10) === " 08:11:08"
                // substr(0,10) === "2017-02-12"
                date.toString().substr(0,10)
            )
        },

@xyzdata
Copy link
Author

xyzdata commented Jul 13, 2017

image

react-redux

@xyzdata
Copy link
Author

xyzdata commented Jul 13, 2017

Layout

Layout:布局容器,其下可嵌套 Header Sider Content Footer 或 Layout 本身,可以放在任何父容器中。
Header:顶部布局,自带默认样式,其下可嵌套任何元素,只能放在 Layout 中。
Sider:侧边栏,自带默认样式及基本功能,其下可嵌套任何元素,只能放在 Layout 中。
Content:内容部分,自带默认样式,其下可嵌套任何元素,只能放在 Layout 中。
Footer:底部布局,自带默认样式,其下可嵌套任何元素,只能放在 Layout 中。

https://ant.design/components/layout-cn/

https://ant.design/components/tabs-cn/

@xyzdata
Copy link
Author

xyzdata commented Jul 14, 2017

React table

React table with column sorting (asc, desc), input filtering, and row ordering

https://codereview.stackexchange.com/questions/106908/react-table-with-column-sorting-asc-desc-input-filtering-and-row-ordering

https://codepen.io/reggi/pen/KdqdMW?editors=001

2.10.6/moment.min.js, react-with-addons-0.13.3.js, 3.0.1/lodash.min.js

function recursive(collection, callback, recursiveIndexCb) {
    var indexed = (recursiveIndexCb)
        ? recursive.recursiveIndex(collection, recursiveIndexCb)
        : false;
    var meta = (recursiveIndexCb)
        ? recursive.meta(indexed)
        : false;
    function wrapper(callback) {
        return function () {
            var args = _.values(arguments);
            var id = args[1];
            var recursiveId = (this.id === 0)
                ? id.toString()
                : id + '.' + this.id;
            args.push(recursiveId);
            return callback.apply({
                'meta': meta,
                'indexed': indexed
            }, args);
        }.bind(this);
    }
    var recursed = false;
    function _recursive(collection, callback) {
        recursed = (recursed === false)
            ? 0
            : recursed + 1;
        return callback(collection, _.partialRight(_recursive, callback), wrapper.bind({'id': recursed}));
    }
    return _recursive(collection, callback);
};

recursive.compare = function (n1, n2) {
    var path1 = n1.split('.');
    var path2 = n2.split('.');
    var maxLen = Math.max(path1.length, path2.length);
    var i = 0;
    while (i < maxLen) {
        if (!path1[i] || + path1[i] < + path2[i]) {
            return -1;
        }
        if (!path2[i] || + path1[i] > + path2[i]) {
            return 1;
        }
        i++;
    }
    return 0;
};

recursive.subset = function (ids, id) {
    return _.filter(ids, function (_id) {
        var _idArr = _id.split('.');
        var idArr = id.split('.');
        var _idChop = _.take(_idArr, _idArr.length - 1).join('.');
        var idChop = _.take(idArr, idArr.length - 1).join('.');
        if (_idChop === idChop) 
            return true;
        return false;
    });
};
/* 
wrapper(function (node, id, nodes, recursiveId) {
    if (node.children) 
        recursive(node.children);
    result[recursiveId] = node;
}
 */
recursive.recursiveIndex = function (nodes, recursiveIndexCb) {
    var result = {};
    recursive.recursive(nodes, function (node, recursive, wrapper) {
        _.each(node, function (value, key, values, rId) {
            var args = _.values(arguments);
            var val = recursiveIndexCb.apply(null, args);
            result[rId] = val;
        });
    }, true);
    return result;
};

recursive.meta = function (indexed) {
    var ids = _.keys(indexed);
    return function (id, distance) {
        distance = (distance)
            ? distance
            : 1;
        ids = ids.sort(recursive.compare);
        var idIndex = ids.indexOf(id);
        var meta = {};
        meta.prev = (ids[idIndex - distance])
            ? ids[idIndex - distance]
            : false;
        meta.next = (ids[idIndex + distance])
            ? ids[idIndex + distance]
            : false;
        var idsSubset = recursive.subset(ids, id);
        var idSubsetIndex = idsSubset.indexOf(id);
        meta.prevSibling = (idsSubset[idSubsetIndex - distance])
            ? idsSubset[idSubsetIndex - distance]
            : false;
        meta.nextSibling = (idsSubset[idSubsetIndex + distance])
            ? idsSubset[idSubsetIndex + distance]
            : false
        return meta;
    };
};

var st = {};

/** ensure children or reactFragment is an array of reactElements */
st.ensureChildrenArray = function (children) {
    var results = [];
    React.Children.forEach(children, function (child) {
            results.push(child);
        });
    return results;
};

// I know this is bad practice but I'm not sure of an alternative
/** map children and clone with props assign random key */
st.cloneChildren = function (children) {
    return React.Children.map(children, function (child, i) {
        return React.addons.cloneWithProps(child, {'key': Math.random()});
    });
};

/** sorts chilren based on column property */
st.sortChildren = function (children, column, format, direction) {
    children = st.ensureChildrenArray(children);
    var results = _.sortBy(children, function (item) {
        var match = _.find(item.props.children, function (child) {
            return (child.props.column === column);
        });
        if (format) 
            return moment(match.props.children, format).toDate();
        return match.props.children;
    })
    if (direction === 'asc') 
        results = results.reverse();
    return st.cloneChildren(results);
};

/** filters array of children based on value */
st.filterChildren = function (children, filterValue, caseSensitive) {
    children = st.ensureChildrenArray(children);
    var results = recursive(children, function (children, recursion, wrapper) {
        return _.filter(children, wrapper(function (child) {
            if (typeof child.props.children !== 'string') {
                var result = recursion(child.props.children);
                return Boolean(result.length);
            } else {
                var flag = (caseSensitive)
                    ? ''
                    : 'i';
                var pattern = new RegExp(filterValue, flag);
                var value = child.props.children.match(pattern);
                return value;
            }
        }))
    });
    return st.cloneChildren(results);
};

st.objectWithOneProp = function (prop, val) {
    var temp = {};
    temp[prop] = val;
    return temp;
};

st.invokeSort = function (column, dateFormat) {
    return function (event) {
        var data = {};
        data.stateCycle = ['desc', 'asc'];
        data.column = 'header_' + column;
        data.headerState = this.state[data.column];
        data.index = _.indexOf(data.stateCycle, data.headerState);
        data.nextState = (data.index !== (data.stateCycle.length - 1))
            ? data.stateCycle[data.index + 1]
            : data.stateCycle[0];
        data.setState = st.objectWithOneProp('header_' + column, data.nextState);
        data.sortedChildren = st.sortChildren(this.refs.tbody.state.children, column, dateFormat, data.headerState);
        this.setState(data.setState)
        this.refs.tbody.setState({children: data.sortedChildren});
    }.bind(this);
};

st.invokeFilter = function (event) {
    var data = {};
    data.filteredChildren = st.filterChildren(this.refs.tbody.props.children, event.target.value, false);
    this.refs.tbody.setState({children: data.filteredChildren});
};

st.onMountOrder = function (elm) {
    return _.sortBy(elm.state.children, function (item) {
        return item.props.column;
    });
};



var StatefulChildren = React.createClass({
    displayName: 'StatefulChildren',
    propTypes: {
        children: React.PropTypes.node,
        element: React.PropTypes.string,
        defaultElement: React.PropTypes.string,
        modify: React.PropTypes.array || React.PropTypes.func
    },
    getDefaultProps: function () {
        return {defaultElement: 'span'};
    },
    getInitialState: function () {
        return {children: this.props.children};
    },
    componentWillMount: function () {
        if (typeof this.props.modify === 'function') {
            this.setState({
                children: this.props.modify(this)
            })
        } else if (this.props.modify) {
            this.props.modify.forEach(
                function (mod) {
                    this.setState({children: mod(this)})
                }.bind(this)
            );
        }
    },
    render: function () {
        var props = _.omit(this.props, ['element', 'defaultElement', 'children']);
        if (this.props.element) {
            return React.createElement(this.props.element, props, this.state.children);
        } else if (typeof this.state.children === 'string' || !React.isValidElement(this.state.children)) {
            return React.createElement(this.props.defaultElement, props, this.state.children);
        } else {
            return this.state.children;
        }
    }
})

var Table = React.createClass({
    getInitialState: function () {
        return {
            'header_date': 'desc',
            'header_name': 'desc'
        }
    },
    render: function () {
        return (
            <div>
                <input type='text' onChange={st.invokeFilter.bind(this)}/>
                <table>
                    <StatefulChildren element='thead' modify={[st.onMountOrder.bind(this)]}>
                        <th column='date' onClick={st.invokeSort.bind(this)('date', 'MMMM Do, YYYY')}>Date</th>
                        <th column='name' onClick={st.invokeSort.bind(this)('name')}>Name</th>
                    </StatefulChildren>
                    <StatefulChildren element='tbody' ref='tbody'>
                        <StatefulChildren element='tr' modify={[st.onMountOrder.bind(this)]}>
                        <td column='name'>Velma</td>
                        <td column='date'>October 28th, 2014</td>
                        </StatefulChildren>
                        <StatefulChildren element='tr' modify={[st.onMountOrder.bind(this)]}>
                        <td column='date'>October 4th, 2015</td>
                        <td column='name'>Julie</td>
                        </StatefulChildren>
                        <StatefulChildren element='tr' modify={[st.onMountOrder.bind(this)]}>
                        <td column='date'>December 1th, 2011</td>
                        <td column='name'>Becky</td>
                        </StatefulChildren>
                        <StatefulChildren element='tr' modify={[st.onMountOrder.bind(this)]}>
                        <td column='date'>January 4th, 2015</td>
                        <td column='name'>Ashley</td>
                        </StatefulChildren>
                    </StatefulChildren>
                </table>
            </div>
        )
    }
});

// React.render(<Table/>, document.getElementById('container'));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment