Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How well does T3 work with ReactJS? #42

Closed
ktei opened this issue Apr 23, 2015 · 12 comments
Closed

How well does T3 work with ReactJS? #42

ktei opened this issue Apr 23, 2015 · 12 comments

Comments

@ktei
Copy link

ktei commented Apr 23, 2015

I love ReactJS but it basically only renders View and I still need the other things like event handling, etc. It seems T3 has very simple and elegant idea. I tried a bit tonight with T3 + ReactJS. One thing I did notice is that when I run Box.Application.init, it expects that all the elements with data-module attributes are already added to Box.Application.

It seems I've found that componentWillMount is a good place to write Box.Application.addModule and in componentDidMount, you can run Box.Application.init.

I attached my code below. Do you think this is the proper way to use T3 and ReactJS? Thank you!

           var CommentBox = React.createClass({
                getInitialState: function() {
                    return {
                        showSubmit: true
                    };
                },

                componentWillMount: function() {
                    var r = this;
                    Box.Application.addModule('comment-box', function(context) {

                        // private methods here
                        return {

                            init: function() {
                                // capture the reference when the module is started
                                moduleEl = context.getElement();
                                console.log(moduleEl);
                            },

                            onclick: function(event, element, elementType) {
                                if (elementType == 'submit-btn') {
                                    r.setState({showSubmit: false});
                                    context.broadcast('submit', 'Submitted!');
                                } else if (elementType == 'close-btn') {
                                    r.setState({showSubmit: true});
                                    context.broadcast('close', 'Closed!');
                                }
                            }

                        };
                    });
                },

                render: function() {
                    return (
                        <div className="commentBox" data-module="comment-box">
                            <input type="text" />
                            {this.state.showSubmit ? <button data-type="submit-btn">Submit</button> : <button type="button" data-type="close-btn">Close</button>}
                        </div>
                    );
                }
            });

            var Article = React.createClass({
                getInitialState: function() {
                    return {text: ''};
                },

                componentWillMount: function() {
                    var r = this;
                    Box.Application.addModule('article', function(context) {
                        return {
                            messages: ['submit', 'close'],

                            onmessage: function(name, data) {
                                if (name == 'submit') {
                                    r.setState({text: 'submit: ' + data});
                                } else if (name == 'close') {
                                    r.setState({text: 'close: ' + data});
                                }
                            }
                        };
                    });
                },

                render: function() {
                    return (
                        <div data-module="article">
                            <p>{this.state.text}</p>
                        </div>
                    );
                }
            });

            var UI = React.createClass({
                componentDidMount: function() {
                    Box.Application.init({debug: true});
                },

                render: function() {
                    return (
                        <div>
                            <CommentBox />
                            <Article />
                        </div>
                    );
                }
            });

            React.render(
                <UI />,
                document.getElementById('content')
            );
@priyajeet
Copy link
Contributor

I think there are probably multiple ways, I am unsure of the best approach, you can also use services to return react components...

https://gist.github.com/priyajeet/c2731789d3a688ee85a9

In this case you would need one parent module that is responsible for listening to messages via onmessage(). However all others are service subcomponents that broadcast messages via application.broadcast(). So you simulate a unidirectional data flow and render chain. The parent module does business logic (server call or whatever) and when it comes to re-rendering the UI, just calls some render function. React then takes over deciding what part of the virtual dom needs re-rendering.

@nzakas
Copy link
Contributor

nzakas commented Apr 23, 2015

We haven't built much using T3 and React to this point, so we don't have any best practices yet. That said, we tend to think about things more in the way @priyajeet 's example shows, where a T3 module "has a" React class rather than the other way around.

@nzakas nzakas closed this as completed Apr 23, 2015
@ktei
Copy link
Author

ktei commented Apr 23, 2015

Thank you Nicholas. This helps me a lot. Hope you guys may give some best
practice example later. The way I see it, these two can make powerful
combination
On 24/04/2015 1:32 AM, "Nicholas C. Zakas" notifications@github.com wrote:

We haven't built much using T3 and React to this point, so we don't have
any best practices yet. That said, we tend to think about things more in
the way @priyajeet https://github.com/priyajeet 's example shows, where
a T3 module "has a" React class rather than the other way around.


Reply to this email directly or view it on GitHub
#42 (comment).

@nzakas
Copy link
Contributor

nzakas commented Apr 24, 2015

We will as we learn more. Feel free to share your experiences as well.

@ktei
Copy link
Author

ktei commented Apr 24, 2015

@nzakas I saw this in documentation that:
Box.Application.init - Initializes the application. This will start modules on the page.
Also, Box.Application.start - Begins the lifecycle of a module (registers and binds listeners).

Here, does start do the same thing as init, though init may do a little bit more, but they can both start modules? In other words, when I use start to start a module, or call init to start that module (by starting all modules on the page), is there any difference?

This also brings another question: often you'll find that not all modules are presented in your html when the application starts, and instead, they will be rendered in run-time. For example, after a search, you may add a new module and render its HTML to your page, so when this happens, do I call Application.start(theModule) or Application.init again?

Sorry that I have so many questions but I'd love to learn more of this framework. And Thank you very much!

@ktei
Copy link
Author

ktei commented Apr 24, 2015

@priyajeet Thank you for the example. I think your example makes sense. It shows the core of T3. My question (the one I asked above) is: how do you ensure that after componentDidMount, the module is added to Box.Application? If you see my question above, you'll understand what I'm asking

@priyajeet
Copy link
Contributor

So when I tried using React to create the todo list, I did end up having one main bootstrapping app component outside a T3 module that did the init. I am not sure if this is the best way.

var TodoApp = React.createClass({displayName: "TodoApp",
    componentDidMount: function() {
        Box.Application.init({
            debug: true
        });
    },
    render: function() {
        return (
            React.createElement("div", {"data-module": "todo-list"})
        );
    }
});
React.render(
    React.createElement(TodoApp, null),
    document.body
);

@ktei
Copy link
Author

ktei commented Apr 24, 2015

@priyajeet That works in this case. However what I'm not very sure about is that when you render a React component in run-time (after your application starts), then what method will you call?

From the look of the document I have read, it seems you can call Box.Application.start(aModule) to start the specific module, though I'm not sure if this is right. Too sleepy tonight (Australia time), maybe I'll try something tomorrow ; )

@priyajeet
Copy link
Contributor

Yeah, so we do some of that stuff internally via a dom utility service. That just wraps innerHTML setting.

dom.setHTML(element, '<div data-module="todo-list"></div>')
Application.addService('dom', function(application) {
    return {
        setHTML: function(element, html) {
            // Safe guard against nulls and special Node objects like DocumentNode
            if (element && element.nodeType === 1) {
                application.stopAll(element);
                element.innerHTML = html;
                application.startAll(element);
            } else {
                application.reportError(new Error('setHTML() requires a valid DOM element'));
            }
        }
    };
});

@nzakas
Copy link
Contributor

nzakas commented Apr 24, 2015

@ktei Box.Application.init() is meant to be called once and it starts everything on the page. Box.Application.start() can be called at any point to start a particular module. Box.Application.startAll() can be called over a large area that contains multiple modules.

@ktei
Copy link
Author

ktei commented Apr 24, 2015

Thank you Nicholas, that's perfect:-)
On 25/04/2015 2:31 AM, "Nicholas C. Zakas" notifications@github.com wrote:

@ktei https://github.com/ktei Box.Application.init() is meant to be
called once and it starts everything on the page. Box.Application.start()
can be called at any point to start a particular module.
Box.Application.startAll() can be called over a large area that contains
multiple modules.


Reply to this email directly or view it on GitHub
#42 (comment).

@dreki
Copy link

dreki commented Feb 11, 2018

Here's a simple example of how to use React within a T3 module.

import {Application} from 't3js';
import React from 'react';
import {render} from 'react-dom';

Application.addModule('log-in', (context) => {
  class LogIn extends React.Component {
    static init() {
      render(<LogIn/>, context.getElement());
    }
    render() {
      return <h1>Hello React</h1>
    }
  }

  return LogIn;
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants