Closed joshrtay closed 9 years ago
I really like those ideas. I think I'm leaning more towards context
like React, but we will need to update the way the dirty check works for components.
Having the apps as components is interesting... If we went the context route we actually wouldn't even need the app
concept and just render vnodes directly (since the app is really just for setting data on):
render(
document.body,
<div>Hello!</div>
)
Which we can allow currying with so we can just do:
let renderBody = render(document.body)
renderBody(<div>Hello!</div>)
renderBody(<span>World</span>)
This would mean we wouldn't need to do something like option 1 if the tree
is gone. I can't think of a really nice way of doing context that doesn't seem like magic or global variables. There could be the case where some deeply nested component needs some context value set at the top-level but then you'd forget to set it at some intermediate level.
So we'd need to decide if that magic and scope is more valuable than just have a single "environment" context like we do now.
I'm +1 for option 2 if we can figure it out nicely. With sources, validation, and potentially the vdom creation out of core we'd have a much simpler library that other tools could build on top of.
@lancejpollard you'll be interested in this thread :)
New idea for sources:
If sources are just observables and initalState can optionally return an observable then sources can be implemented really easily and generally. If you wanted to use flyd it might look something like:
import {render} from 'dekujs/deku'
import {stream} from 'paldepind/flyd`
let B = {
initialState: function ({context}) {
return stream([context.currentUser], function() {
return {name: context.currentUser()}
})
},
render: function ({state}) {
return <div>My name is: {state.name}</div>;
}
}
let currentUser = stream()
let A = {
context: {
return {currentUser: currentUser}
},
render: function() {
return <B/>
}
}
// Output: "My name is:"
render(<A/>, document.body)
// Output: "My name is: Tio"
currentUser('Tio')
@lancejpollard Just realized if props
is a stream then props
and state
can safely be merged #144. Although it does significantly change the API.
import {stream} from 'paldepind/flyd'
export function initialState({props}) {
return stream([props], function() {
return {
open: props().open,
onCancel: props().onCancel,
onConfirm: props().onConfirm
}
})
}
export function render({state}) {
let {onCancel, onConfirm, open} = state
if (!open) return <noscript></noscript>
return (
<div class='Dialog'>
<div class='Dialog-title'>Confirm?</div>
<div class='Dialog-button' onClick={cancel}>Cancel</div>
<div class='Dialog-button' onClick={confirm}>Confirm</div>
</div>
);
function cancel() {
setState({ open: false })
onCancel()
}
function confirm() {
setState({ open: false })
onConfirm()
}
}
@dominicbarnes comment about extracting sources from propTypes got me thinking about enhancing sources to allow for namespaces. The problem with sources as they stand is that they can only be added at the top level, so it's really impossible to make self contained sub apps that compose. What I'd really like to be able to do is assemble large client side apps the same I assemble express apps, just mount the sub apps at various endpoints and provide them with a render function.
With this setup it would be preferable if sub apps had self contained or inherited sources to avoid naming collisions. We could achieve this by making sources work a bit more like context in react. Instead of being restricted to setting sources at the root, all components can set sources that their children can then request. This could be implemented by letting components set child sources or by letting deku apps compose.
App as component (option 1)
Deku apps are themselves components.
Sources as context (option 2)
This approach is similar to the context api in react.