1

I'm very new to React and might've bit off too much.

In my app, I'm trying to make a document builder (sort of like building emails in mailchimp). When you click on an element's icon, the markup for that element will be added to the document page.

Conceptually, I'm not sure of the best way to do this in React. My questions include:

  • How do you track which elements have been added to the document?
  • Do I manage state at the app level?
  • Eventually, I'll want the user to customize styles of each element added to the document, where should that code live?
  • It seems like order of the data and functions matters (sometimes it breaks when this order changes). Is there a better way to organize this?

For now, any advice on the 'react' way to set this up would be appreciated.

My main JS:

// ---------------------------------------------------------
// Elements
// ---------------------------------------------------------
var TextBlock = React.createClass({
    render: function() {
        return (
            <p>Lorem ipsum ... laborum.</p>
        );
    }
});

var ImageBlock = React.createClass({
    render: function() {
        return (
            <img src="placehold.it/360x240" alt="Image" />
        );
    }
});

var HeadingBlock = React.createClass({
    render: function() {
        return (
            <h3>Heading Here</h3>
        );
    }
});

var TableBlock = React.createClass({
    render: function() {
        return (
            <table>
                ... Table Markup Here ...
            </table>
        );
    }
});

var ColumnsBlock = React.createClass({
    render: function() {
        return (
            <div class="row">
                <div class="col-xs-4">Col 1</div>
                <div class="col-xs-4">Col 2</div>
                <div class="col-xs-4">Col 3</div>
            </div>
        );
    }
});

var Divider = React.createClass({
    render: function() {
        return (
            <hr />
        );
    }
});

var elementList = [
    {
        'name': 'Text',
        'icon': 'align-left',
        'html': <TextBlock />
    },
    {
        'name': 'Image',
        'icon': 'picture-o',
        'html': <ImageBlock />
    },
    {
        'name': 'Heading',
        'icon': 'header',
        'html': <HeadingBlock />
    },
    {
        'name': 'Table',
        'icon': 'table',
        'html': <TableBlock />
    },
    {
        'name': 'Column',
        'icon': 'columns',
        'html': <ColumnsBlock />
    },
    {
        'name': 'Divider',
        'icon': 'times',
        'html': <Divider />
    },
];

var Element = React.createClass({
    handleClick: function(e){
        e.preventDefault();
        this.props.handleClick();
    },

    render: function() {
        return (
            <li className="element" onClick={this.handleClick}>
                <i className={'fa fa-' + this.props.icon}></i>
                <span className="element__name">{this.props.name}</span>
            </li>
        );
    }
});

var Elements = React.createClass({
    getInitialState: function () {
        return {
            elementList: elementList,
        };
    },

    handleClick: function(index, element){
        console.log(element.html);
    },

    render: function() {
        return (
            <ul className="list-unstyled">
                {this.state.elementList.map(function(element, index) {
                    return (
                        <Element
                            key={index}
                            handleClick={this.handleClick.bind(this, index, element)}
                            name={element.name}
                            icon={element.icon}
                        />
                    );
                }.bind(this))}
            </ul>
        );
    }
});

// ---------------------------------------------------------
// Tabs
// ---------------------------------------------------------

var Tab = React.createClass({
    handleClick: function(e){
        e.preventDefault();
        this.props.handleClick();
    },

    render: function() {
        return (
            <a className={this.props.isCurrent ? 'btn btn-primary' : 'btn btn-default'} onClick={this.handleClick}>
                {this.props.name}
            </a>
        );
    }
});

var Tabs = React.createClass({
    handleClick: function(tab){
        this.props.changeTab(tab);
    },

    render: function(){
        return (
            <div className="text-center">
                <nav className="btn-group">
                    {this.props.tabList.map(function(tab, index) {
                        return (
                            <Tab
                                key={index}
                                handleClick={this.handleClick.bind(this, index)}
                                name={tab.name}
                                isCurrent={(this.props.currentTab === index)}
                             />
                        );
                    }.bind(this))}
                </nav>
            </div>
        );
    }
});

var Settings = React.createClass({
    render: function() {
        return (
            <div>test settings</div>
        );
    }
});

var tabList = [
    {
        'name': 'Content',
        'content': <Elements />
    },
    {
        'name': 'Settings',
        'content': <Settings />
    }
];

var Document = React.createClass({
    render: function() {
        return (
            <article className="document__frame">
                <div className="document">test</div>
            </article>
        );
    }
});

var App = React.createClass({
    getInitialState: function () {
        return {
            tabList: tabList,
            currentTab: 0
        };
    },

    changeTab: function(index) {
        this.setState({ currentTab: index });
    },

    render: function() {
        return (
            <div>
                <Document />
                <div className="control">
                    <Tabs
                        currentTab={this.state.currentTab}
                        tabList={this.state.tabList}
                        changeTab={this.changeTab}
                    />
                    <div className="control__panel">
                        {this.state.tabList[this.state.currentTab].content}
                    </div>
                </div>
            </div>
        );
    }
});

ReactDOM.render(
    <App />,
    document.getElementById('content')
);

1 Answer 1

1

The react way would be to use a container component that handles all of your state changes.

In addition each component has a lifecycle and lifecycle methods that you can use to change your state

Your Questions:

  • How do you track which elements have been added to the document?

This can be done with componentDidMount()

  • Do I manage state at the app level?

Yes

  • Eventually, I'll want the user to customize styles of each element added to the document, where should that code live?

Its up to you, you can write 'inline styles' or require/import styles from a different file

  • It seems like order of the data and functions matters (sometimes it breaks when this order changes). Is there a better way to organize this?

Yes, see below

On containers:


https://medium.com/@learnreact/container-components-c0e67432e005#.igjce0wof


Here are the relevant links in the React docs on lifecycle:


https://facebook.github.io/react/docs/component-specs.html#lifecycle-methods:

Sign up to request clarification or add additional context in comments.

3 Comments

Good info, thank you! I guess the question of styling more has to do with the fact that the elements that can be added to the document are templates. For instance, there could be multiple text blocks per document, each with different styles. So inline styles are the way to go, but I wasn't sure where to store this data. I guess creating a 'createdElements' array and adding styling data to that is what I'd probably do.
No prob! happy to help. I really like having a separate file for styles, basically just a javascript object that is imported into your react component file and then you can just pass that in to your component: var transparentBG = require('../styles').trasparentBG then in the component: '<MyComponent style={transparentBG} />` - dont forget to export the styles obj using module.exports = styles;
Ah, that'd be a good way to do it. I'm more of a web designer than js developer, so I really like styles being in separate files myself. About the only part of react I haven't been sure about.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.