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

Place base components as sibling files to those which extend them; composition vs inheritance #223

Open
3 of 19 tasks
thejmazz opened this issue Mar 24, 2017 · 1 comment
Open
3 of 19 tasks

Comments

@thejmazz
Copy link
Contributor

thejmazz commented Mar 24, 2017

I think it would make more sense for src/core/LightComponent.js to be at src/components/lights/LightComponent.js - there is this (somewhat arbitrary) assumption about "core ness". At least when digging through the hierarchy source, helps to not have to jump around (if you do not know where it is). And since we can export everything as named exports it behaves the same to end user.

Naturally, this would lend to Component.js sitting near/or at the/a root of this tree

From a purely discussion perspective, I'm also curious if these hierarchical properties can be defined using a composable technique rather than extending. The responsibilities on an inheritance class seem to include "assign onto static defaults" at least. In the React community, it is generally recommend to only ever decorate a React.Component rather than extending one. (Except maybe, if you need to fix for a specific one-off use case with a 3rd party component). If we were to take a "react component like" stance I think it is important we address deviations from expectations given a react component API and what those developers may be accustomed with. In that case, the "whs to react" tool would not introduce abnormalities as to what developers may be expecting from a JSX+react-like classes API. Whats more, is patterns for handling async dependencies in components which already exist can be compared/contrasted (or mirrored) for our implementation. For example, having Component.add(<component>) return a Promise<Component> coupled with Component.addTo delegating to add is somewhat confusing - and feels like the "solution" to async dependencies has been introduced without careful consideration of the use cases, and if this deviation from expected behaviour is worth the complexity (i.e. expected behaviour of add in Three.js API). Is there a good example of using async addTos? For referencing *.obj models directly right? - Which is useful and quite neat. But maybe declare beforehand whether or not dependency is asynchronous (see: react-resolver), and by default have it be synchronous. Aiming for React also clarifies goals somewhat - which I think is super important when multiple people attempt to implement abstractions with minimal leakage.

In general: I think it is safe to say that the benefit of using composition over inheritance is that it is easier to string more separated components together, since they need to define which item they affect, and if this is respected their should be no conflicts. However I know that game programming seems to use a lot of OOP Classes (Unity, Unreal). I have not ever developed a game either so its very possible I am pre optimizing these architectural considerations unnecessarily.

Addendum: I think an analogy for smart vs dumb components à la react-redux could be where

  • smart components manage params and updating params, loops - values passed into "pure render functions"
  • pure render functions do not handle any global state, only send data through events up - e.g. click or doubleClick events (and streams of events can be processed into atomic chunks with Observables via RxJS 5), but can also store a local state

E.g.

// please correct me on mistakes!

// my intention is to have a module Rotator
// an instance of Rotator called rotator is made
// which is passed as a module to n components
// and updates rotation of each component to same value every tick

// "container" sends new props to pure render function
class Rotator extends Module {
  constructor(props) {
    super(props)

    this.rotation = 0
    this.loops = []
  }

  integrate(self) {
    this.loops.push(new Loop({ tick }) => {
      self.rotation.x += tick*Math.PI/180 * 0.64
    }))
  }
}
// perhaps pass app (or any component supporting holding loops) into Rotator's params
// then this code can be inside module
const rotator = new Rotator()
rotator.loops.forEach((loop) => loop.addTo(app))

// "pure render function"
// in this case, render is hidden from declarative code
// there is an implicit assumption that setting the "rotation" property on this class
// will affect how it appears in the rendered scene. rotator could be abstracted to
// potentially make that portion more declarative
const box = new Box({
  modules: [ rotator ]
})

This is not to say that setting a loop and running it within a component is not a good approach. Imagine the render function of a component is called every single frame in the render loop if that component is visible (perhaps it could get globalTick if it so desired as a default prop passed to render for example). The benefit of separating, defining the associations, and what changes they make to a global state (any state of which two components will incorporate into their lifecycle), is that it is then you can be assured of the condition of that shared variable at any time as well as having a paper trail of all modifications to that referenced location within the state tree. This only provides benefit when multiple items listen to this same reference and can also emit events (which can be transformed into actions) which modify the value of reference at that position within the state (immutability). It could even still be that a simple mishmash of getters/setters between two class could suffice for simple, isolated scenarios.

I don't mean to criticizing of the architecture or patterns in this library, but rather hope that we can develop an API that really can be translated into JSX components and work in view libraries according to expectations from React/Vue. That would be incredible - like an enhanced version of deck.gl but where JSX components are "auto made" with a system that is backed by JS components. Similar to A-frame, I'm curious their approach now (i.e. self-made vs. following react/vue expected api conventions). Also a consideration should probably be WebComponents however I am not familiar with them. I think it is safe using JSX as a "description target" (backed by typed prop types declarations on components being what they receive in the constructor) since that is now being used in multiple libraries, and its syntax tree can be directly utilized/manipulated if more in depth practices are required.

Version:
  • v2.x.x
  • v1.x.x
Issue type:
  • Bug
  • Proposal/Enhancement
  • Question
  • Discussion

Tested on:
Desktop
  • Chrome
  • Chrome Canary
  • Chrome dev-channel
  • Firefox
  • Opera
  • Microsoft IE
  • Microsoft Edge
Android
  • Chrome
  • Firefox
  • Opera
IOS
  • Chrome
  • Firefox
  • Opera
@hirako2000
Copy link
Collaborator

A good read: there

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

No branches or pull requests

2 participants