richardanaya / webblock

The simplest way to make expressive and powerful web components
MIT License
30 stars 0 forks source link

State vs. data #3

Closed mbrowne closed 8 years ago

mbrowne commented 8 years ago

I like how React differentiates between state and immutable data (props). It seems that with this library there is no such distinction. Do you see making that distinction as outside the scope of this library? e.g. I could just create top-level attributes called state and props for my components (or maybe state and data since in the real world and by its traditional definition, "data" is inherently immutable). But on the other hand, maybe this library could support such an approach directly. For example, after enabling this distinction via some optional setting, the library could provide a setState() method for every component like React does. I'm happy to try my hand at implementing this myself and submitting a pull request, but I thought I'd first ask if you think it would be a good idea (especially since I'm new to React and web components).

richardanaya commented 8 years ago

Thanks for your interest. I'm trying to think about your desires in the context of an html web component. The attributes really are the interface, and I worry I've stretched the interface a little far with types and backing properties. By default right now attributes are stateful. What do you think about the concept of read only attributes that can't be changed once the element is initialized?

On Jul 26, 2016 12:25 PM, "Matt Browne" notifications@github.com wrote:

I like how React differentiates between state and immutable data (props). It seems that with this library there is no such distinction. Do you see making that distinction as outside the scope of this library? e.g. I could just create top-level attributes called state and props for my components (or maybe state and data since in the real world and by its traditional definition, "data" is inherently immutable). But on the other hand, maybe this library could support such an approach directly. For example, after enabling this distinction via some optional setting, the library could provide a setState() method for every component like React does. I'm happy to try my hand at implementing this myself and submitting a pull request, but I thought I'd first ask if you think it would be a good idea (especially since I'm new to React and web components).

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/richardanaya/webblock/issues/3, or mute the thread https://github.com/notifications/unsubscribe-auth/AAR8mtNnOFL7o48te1wYctT-V41-L3sQks5qZkMTgaJpZM4JVY5Q .

mbrowne commented 8 years ago

The reason I like React's distinction between state and props is that it forces the programmer to think about what really needs to be stateful/mutable and what doesn't. I'm becoming increasingly convinced after reading some literature on this topic (for example, this) that keeping the state of the View in sync with the Model, and keeping track of state changes makes GUI code much harder to reason about than it needs to be. IMO React's emphasis on avoiding unnecessary state and side effects is one of its most important innovations (though it's not really a new idea). Of course, time will tell if this actually proves true with my own projects.

In any case, I like your idea of read-only attributes but I would suggest an option for making read-only attributes the default, for those who would like to take this approach (which I imagine would most likely be the preferred approach of people coming from React).

Where I get a bit fuzzy is how and when the component should be re-rendered, and I'm not sure if React's conceptual model can be shoehorned into the conceptual model of web components here. But the important thing is that a component should never change its own props. The only way for props to change in React (if I understand correctly) is if the component is either manually re-rendered with new props, or if it's a child of another component and its parent re-renders it with new props. A component can, of course, change its own state, which is needed for things like input elements.

I'd like to experiment with all of this...I should probably do that before making any more specific suggestions. In the meantime, let me know if you have any follow-up thoughts on this. Thanks!

richardanaya commented 8 years ago

So, just some general feedback:

On Tue, Jul 26, 2016 at 2:49 PM, Matt Browne notifications@github.com wrote:

The reason I like React's distinction between state and props is that it forces the programmer to think about what really needs to be stateful/mutable and what doesn't. I'm becoming increasingly convinced after reading some literature on this topic (for example, this) that keeping the state of the View in sync with the Model, and keeping track of state changes makes GUI code much harder to reason about than it needs to be. IMO React's emphasis on avoiding unnecessary state and side effects is one of its most important innovations (though it's not really a new idea). Of course, time will tell if this actually proves true with my own projects.

In any case, I like your idea of read-only attributes but I would suggest an option for making read-only attributes the default, for those who would like to take this approach (which I imagine would most likely be the preferred approach of people coming from React).

Where I get a bit fuzzy is how and when the component should be re-rendered, and I'm not sure if React's conceptual model can be shoehorned into the conceptual model of web components here. But the important thing is that a component should never change its own props. The only way for props to change in React (if I understand correctly) is if the component is either manually re-rendered with new props, or if it's a child of another component and its parent re-renders it with new props. A component can, of course, change its own state, which is needed for things like input elements.

I'd like to experiment with all of this...I should probably do that before making any more specific suggestions. In the meantime, let me know if you have any follow-up thoughts on this. Thanks!

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/richardanaya/webblock/issues/3#issuecomment-235383681, or mute the thread https://github.com/notifications/unsubscribe-auth/AAR8moPOY5Fylx5-IgDJcmqobo4O4DpQks5qZmTEgaJpZM4JVY5Q .

richardanaya commented 8 years ago

I think you might really like Vue.js

On Jul 26, 2016 4:38 PM, "Richard Anaya" richard.anaya@gmail.com wrote:

So, just some general feedback:

  • I think even though immutable data is popular, and there are some really amazing optimizations one can do, just even having minimal dom changes makes React very valuable
  • In webblock, I do some very minimal equality diff checking on attribute changes (either via the attribute or the backing property I create) to prevent unnecessary renders
  • Webblock's primary purpose was to create more efficient internal updating of a web component. I really don't know who will consume my web component, and i'm hesitant of enforcing any dogma to my components external api via the attributes. The web component might be used by a react person, an angular person, or some other framework (or none at all). Web components in general are meant to be dumb html drop ins that have radically more power. My exposure of mirror properties that bypass the string conversion of going through text attributes was about as far as I think I want to take the external api.

On Tue, Jul 26, 2016 at 2:49 PM, Matt Browne notifications@github.com wrote:

The reason I like React's distinction between state and props is that it forces the programmer to think about what really needs to be stateful/mutable and what doesn't. I'm becoming increasingly convinced after reading some literature on this topic (for example, this) that keeping the state of the View in sync with the Model, and keeping track of state changes makes GUI code much harder to reason about than it needs to be. IMO React's emphasis on avoiding unnecessary state and side effects is one of its most important innovations (though it's not really a new idea). Of course, time will tell if this actually proves true with my own projects.

In any case, I like your idea of read-only attributes but I would suggest an option for making read-only attributes the default, for those who would like to take this approach (which I imagine would most likely be the preferred approach of people coming from React).

Where I get a bit fuzzy is how and when the component should be re-rendered, and I'm not sure if React's conceptual model can be shoehorned into the conceptual model of web components here. But the important thing is that a component should never change its own props. The only way for props to change in React (if I understand correctly) is if the component is either manually re-rendered with new props, or if it's a child of another component and its parent re-renders it with new props. A component can, of course, change its own state, which is needed for things like input elements.

I'd like to experiment with all of this...I should probably do that before making any more specific suggestions. In the meantime, let me know if you have any follow-up thoughts on this. Thanks!

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/richardanaya/webblock/issues/3#issuecomment-235383681, or mute the thread https://github.com/notifications/unsubscribe-auth/AAR8moPOY5Fylx5-IgDJcmqobo4O4DpQks5qZmTEgaJpZM4JVY5Q .

mbrowne commented 8 years ago

Thanks for the thoughts/explanation. And as to Vue.js, I've glanced at it before but I'll take another look since I've never actually evaluated it. I'm not actually committed to any particular dogma or paradigm, and I suspect that even if my experiments with a more immutable approach prove fruitful, that there will still be some projects where it doesn't fit as well. And in the past I have gotten good results from more traditional MVC approaches and two-way data-binding, such as with Ractive.js. I completely understand why you wouldn't want to favor the paradigm of any one technology in your library, which is intended to be general-purpose and widely interoperable in the same spirit as web components. That's why I suggested the state/data distinction as an option rather than forcing everyone to use it, but now I'm thinking it would be better to implement my ideas as a separate module. I'm hoping I can still build on webblock and have my module act as basically an extension or plugin of webblock. I might have some time to play around with it tomorrow; in any case I'll let you know what I come up with.

mbrowne commented 8 years ago

In case you're curious, here's some code showing the basic idea (but probably quite different from a real implementation):

function ReactWebComponent(componentDef) {
    let passedCreatedCallback = componentDef.createdCallback;
    Object.assign(componentDef, ReactWebComponent.prototype);
    if (passedCreatedCallback) {
        componentDef.createdCallback = function() {
            ReactWebComponent.prototype.createdCallback.call(this);
            passedCreatedCallback.call(this);
        };
    }
    return WebBlock(componentDef);
}

ReactWebComponent.prototype = {
    virtualDom: WebBlock.React(React,ReactDOM),

    createdCallback() {
        //create props object from attributes
        let attrs = this.attributes;
        let props = {};
        for (let attr of attrs) {
            props[attr.nodeName] = attr.nodeValue;
        }
        this.props = Object.freeze(props);

        this.state = {};
    },

    setState(nextState) {
        if (typeof nextState === 'object') {
            Object.assign(this.state, nextState);
        }
        else {
            //TODO
        }
        //re-render the component
        this.__componentRender__();
    }
};

ReactWebComponent({
    tag: 'app-counter',

    createdCallback() {
        this.setState({count: 0});
        this.incrementCount = this.incrementCount.bind(this);
    },

    incrementCount() {
        this.setState({count: this.state.count + 1});
    },

    render() {
        let {props, state} = this;
        return (
            <div>
                <h2>{props.title}</h2>
                <p>{state.count}
                    &nbsp;<button onClick={this.incrementCount}>+</button>
                </p>
            </div>
        );
    },

    attributes: {
        title: String
    }
});

HTML:

<app-counter title="A counter"></app-counter>

That was just to test it out. I would prefer not to re-invent the wheel as I've done here with my own implementation of props and state (e.g. the setState() method) and use React's own methods for that instead.

I searched for other modules that integrate React with web components and I found this: https://github.com/PixelsCommander/ReactiveElements

Since I'm following the React approach regarding props and state rather than just a single attributes collection as with regular web components, I'm thinking that ReactiveElements might be better suited to my needs. So I'm closing this issue for now, and I'll let you know if I still end up using Webblock and if so, how I integrate it in the end.

Thanks again for your responses and feedback.