manage splitterSide for use it as reusable component



  • Hi guys I want to create a new SideMenu component based on OnsenUI splitterSide demo I try this but I don’t know how I can manage my sates and props. I’m new in React.js. Can someone help me to fix this?
    This is my component code now:

    import React, { PropTypes, Component } from 'react';
    import { Page, Icon, List, ListItem, Splitter, SplitterSide, SplitterContent} from 'react-onsenui';
    import page1 from '../pages/page1.jsx';
    import page2 from '../pages/page2.jsx';
    
    class SideMenu extends Component {
        constructor(props) {
            super(props);
    
            this.hide = this.hide.bind(this);
            this.show = this.show.bind(this);
            this.page1 = this.page1.bind(this);
            this.page2 = this.page2.bind(this);
        };
    
        hide() {
            this.props.isOpen = false;
        };
    
        show() {
            this.props.isOpen = true;
        };
    
        goto_page1() {
            this.props.navigator.resetPage({
                component: page1,
                key: 'Page1_Index'
            });
        };
    
        goto_page2() {
            this.props.navigator.resetPage({
                component: page2,
                key: 'Page2_Index'
            });
        };
    
        render() {
            return (
                <Splitter>
                    <SplitterSide
                        style={{
                  boxShadow: '0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23)'
              }}
                        side='left'
                        width={200}
                        collapse={true}
                        isSwipeable={true}
                        isOpen={this.props.isOpen}
                        onClose={this.hide}
                        onOpen={this.show}
                    >
                        <Page>
                            <List
                                dataSource={['page one', 'page two']}
                                renderRow={(title) => {
                                switch(title) {
                                    case "page one":
                                        return (
                                            <ListItem key={title} onClick={this.goto_page1} tappable>
                                                <div>{title}</div>
                                            </ListItem>
                                            );
                                        break;
                                    case "page two":
                                        return (
                                            <ListItem key={title} onClick={this.goto_page2} tappable>
                                                <div>{title}></div>
                                            </ListItem>
                                            );
                                        break;
                                    default:
                                        return (
                                            <ListItem key={title} onClick={this.hide} tappable>
                                                <div>{title}</div>
                                            </ListItem>
                                            );
                                        break;
                                }
                            }}
                            />
                        </Page>
                    </SplitterSide>
                    <SplitterContent>
                        {this.props.children}
                    </SplitterContent>
                </Splitter>
    
            );
        }
    }
    
    SideMenu.propTypes = {
        navigator: PropTypes.object
    };
    
    export default SideMenu;
    
    

    And this is Page1 code:

    import React, { PropTypes, Component } from 'react';
    import { Page, Toolbar, ToolbarButton, Icon} from 'react-onsenui';
    import SideMenu from '../components/SideMenu.jsx';
    
    
    class page1 extends Component {
        constructor(props) {
            super(props);
    
            this.renderToolbar = this.renderToolbar.bind(this);
            this.hide = this.hide.bind(this);
            this.show = this.show.bind(this);
    
            this.state = {
                isOpen: false
            };
        };
    
        renderToolbar() {
            return (
                <Toolbar>
                    <div className='left'>
                        <ToolbarButton onClick={this.show}>
                            <Icon icon='ion-navicon, material:md-menu' />
                        </ToolbarButton>
                    </div>
                    <div className='center'>Page One Title</div>
                </Toolbar>
            );
        };
    
        hide() {
            this.setState({isOpen: false});
        };
        
        show() {
            this.setState({isOpen: true});
        };
    
    
    
        render() {
            return (
            <SideMenu navigator={this.props.navigator} isOpen={this.state.isOpen}>
                <Page renderToolbar={this.renderToolbar}>
                    Page content here
                </Page>
            </SideMenu>
    
            );
        }
    }
    
    page1.propTypes = {
      navigator: PropTypes.object
    };
    
    export default page1;
    
    

    Is my code style is correct?
    How to prevent two time declaration of show and hide function?



  • I change my code as bellow any idea or improvement??

    import { Meteor } from 'meteor/meteor';
    import React, { PropTypes, Component } from 'react';
    import { Page, Icon, List, ListItem, Splitter, SplitterSide, SplitterContent, Toolbar, ToolbarButton, Modal} from 'react-onsenui';
    import { Random } from 'meteor/random';
    import page1 from '../pages/page1.jsx';
    import page2 from '../pages/page2.jsx';
    
    class SideMenu extends Component {
        constructor(props) {
            super(props);
    
            this.renderToolbar = this.renderToolbar.bind(this);
            this.hide = this.hide.bind(this);
            this.show = this.show.bind(this);
            this.goto_page1 = this.goto_page1.bind(this);
            this.goto_page2 = this.goto_page2.bind(this);
    
            this.state = {
                isOpen: false
            };
        };
    
        renderToolbar() {
            return (
                <Toolbar>
                    <div className='left'>
                        <ToolbarButton onClick={this.show}>
                            <Icon icon='ion-navicon, material:md-menu' />
                        </ToolbarButton>
                    </div>
                    <div className='center'>{this.props.pageTitle}</div>
                </Toolbar>
            );
        };
        
        hide() {
            this.setState({isOpen: false});
        };
        
        show() {
            this.setState({isOpen: true});
        };
        
        goto_page1() {
            this.props.navigator.resetPage({
                component: page1,
                key: 'Page1_Index'
            });
        };
    
        goto_page2() {
            this.props.navigator.resetPage({
                component: page2,
                key: 'Page2_Index'
            });
        };
    
        render() {
            return (
                <Splitter>
                    <SplitterSide
                        style={{
                  boxShadow: '0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23)'
              }}
                        side='left'
                        width={200}
                        collapse={true}
                        isSwipeable={true}
                        isOpen={this.state.isOpen}
                        onClose={this.hide}
                        onOpen={this.show}
                    >
                        <Page>
                            <List
                                dataSource={[ 'page one', 'page two']}
                                renderRow={(title) => {
                                switch(title) {
                                    case "page one":
                                        return (
                                            <ListItem key={title} onClick={this.goto_page1} tappable>
                                                <div className='left'>{title}</div>
                                            </ListItem>
                                            );
                                        break;
                                    case "page two":
                                        return (
                                            <ListItem key={title} onClick={this.goto_page2} tappable>
                                                <div className='left'>{title}</div>
                                            </ListItem>
                                            );
                                        break;
                                    default:
                                        return (
                                            <ListItem key={title} onClick={this.hide} tappable>
                                                <div className='left'>{title}</div>
                                            </ListItem>
                                            );
                                        break;
                                }
                            }}
                            />
                        </Page>
                    </SplitterSide>
                    <SplitterContent>
                        <Page renderToolbar={this.renderToolbar} >
                            {this.props.children}
                        </Page>
                        
                    </SplitterContent>
                </Splitter>
    
            );
        }
    }
    
    SideMenu.propTypes = {
        navigator: PropTypes.object.isRequired,
        pageTitle: PropTypes.string.isRequired
    };
    
    export default SideMenu;
    

    I also change my Page1 to:

    import React, { PropTypes, Component } from 'react';
    import { Icon, List, ListItem, ListHeader} from 'react-onsenui';
    import SideMenu from '../components/SideMenu.jsx';
    
    
    class page1 extends Component {
    
        render() {
            return (
            <SideMenu navigator={this.props.navigator} pageTitle="page 1 title">
                    Page content here
            </SideMenu>
    
            );
        }
    }
    
    page1.propTypes = {
      navigator: PropTypes.object
    };
    
    export default page1;
    


  • new version (previous reply) return this error:

    Warning: flattenChildren(...): Encountered two children with the same key, `Page1_Index`. Child keys must be unique; when two children share a key, only the first child will be used.
        in ons-navigator (created by Navigator)
        in Navigator (created by App)
        in Appmodules.js:48127:9
    
    uncaught exception: Splitter side is locked.(unknown)
    

    Any suggestion?



  • @argelius @Fran-Diox @munsterlander guys can you help me to fix this issue?

    Warning: flattenChildren(...): Encountered two children with the same key, `Page1_Index`. Child keys must be unique; when two children share a key, only the first child will be used.
        in ons-navigator (created by Navigator)
        in Navigator (created by App)
        in Appmodules.js:48127:9
    
    uncaught exception: Splitter side is locked.(unknown)
    

    Why children with same key exist when I use navigator.resetPage? Is it a bug?
    What is uncaught exception: Splitter side is locked.(unknown)?



  • Guys I need your help. :cry:



  • @cyclops24 Sorry for not responding. I do not know much about react, so I have been waiting for the Onsen team to respond. I did do some research and found these (maybe they will be of use):

    http://stackoverflow.com/questions/26088810/how-to-avoid-children-with-same-key-when-replacing-state-in-reactjs-component

    http://stackoverflow.com/questions/29382526/react-js-confuse-with-two-children-with-the-same-key


  • Onsen UI

    @cyclops24 Sorry, we’ve been going to some events in Tokyo and didn’t check the forum too much.

    I think you don’t need to worry about Splitter side is locked. It means that it’s already in use so you cannot open it twice. The error is just a rejected promise.

    About the keys, if I’m not mistaken resetPage first pushes a new page on top of the stack and then removes the rest of the pages. Therefore, if you are resetting to an already existing page it’s normal to get that error. It must be done like this because otherwise there would be a flickering in the animation.

    Since this state is only momentarily, perhaps you can also ignore that error. At the end of the animatino you will have unique keys so I guess that’s fine for React. I’m not an expert in React, though.



  • Thanks @munsterlander , @Fran-Diox for your reply,
    @Fran-Diox unfortunately resetPage not working when return Warning: flattenChildren(...): Encountered two children with the same key,... I test almost anything but still my SideMenu not working well after some page changes.
    I don’t know why but sometimes also it’s return TypeError: enterPage is undefined. :cry:



  • @argelius , @munsterlander , @Fran-Diox , @eknoes
    I finally success to reproduce my issue here for provide others check that.
    You can see this CodePen:
    https://codepen.io/anon/pen/ObRzLB?editors=0011
    Open console and see this error: TypeError: enterPage is undefined

    Any Suggestion? Is it a bug or my code is wrong?


  • Onsen UI

    @cyclops24 You must push pages to the navigator, nothing else. page1 renders into SideMenu and that’s not a page. Just wrap it with <Ons.Page> ... </Ons.Page>.



  • Thanks @Fran-Diox It’s fixed man. :wink:



  • @cyclops24: you might consider a more flexible alternative. By that I mean that you manage state via a parent component that automatically gets rerendered whenever you click a new menu item: https://community.onsen.io/topic/821/splitterside/3