-
Notifications
You must be signed in to change notification settings - Fork 595
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
need to documenting model-routing order? #131
Comments
(extracted from discussion in #130) |
|
hahaha, that's all cool ^____^ - glad you managed to solve it! ✨ |
Ehm. So. I was confused in my confusion - we DID encounter a bug as initially described. Apologies again, and thank you @mw222rs for setting me straight! So - this choo
But if I try to port this to
...then I get an error saying action But, if I change the bootstrapping to this:
...then it works! So it does seem to matter in v3 that the |
Hm, I think the issue is that you can't add models after app.start() is run. Because the view doesn't run app.model() until the route is triggered, which is after app.start(). And my guess is that app.start() calling start() on barracks is what registers the reducers. So you'd somehow have to call start on barracks again. Are you opposed to registering the model in index.js? Doing so kind of serves as an index for the app. |
It makes sense that Absolutely it makes sense to register the model in index.js! Much agree that the vantage point over the state and changes is super-useful, much like Redux. It doesn't however make sense if you want to make a reusable contained component. This is what we were playing with in our framework composition comparison, and it was while porting our Choo example for that post that we stumbled upon the changed behaviour. |
Ah, I see what you mean. My understanding would be that you'd want your component to be what react/redux folks call a "dumb component" - it has no concept of state other than the properties it is passed. In react, that's the difference between In choo world, that would be something like: // submission.js
const ConfirmButton = require('./confirm-button')
module.exports = ({ isConfirming = false, onClickSubmit = () => {}, onClickConfirm = () => {} }) => {
const input = html`<input>`
const tree = html`
<div>
${input}
${ConfirmButton({ isConfirming, onClickSubmit, () => onClickConfirm(input.value) })}
</div>`
}
// confirm-button.js
module.exports = ({ isConfirming = false, onClickSubmit = () => {} onClickConfirm = () => {} }) => {
return isConfirming
? html`<button onclick=${onClickConfirm}>Confirm</button>`
: html`<button onclick=${onClickSubmit}>Submit</button>`
}
// view.js
const Submission = require('./submission')
module.exports = (state, prev, send) => {
const submission = Submission({
isConfirming: state.isConfirming,
onClickSubmit: () => send('setSubmit', true),
onClickConfirm: (value) => send('confirm', value)
})
return html`
<div>
${submission}
</div>`
} Caveat: This is practically pseudo-code, I'm writing it very quickly inside this issue, so forgive me if there are any errors in it or if you've already considered it. Hope it helps! |
Forgive me, but I respectfully disagree! Absolutely we should strive to have dumb components wherever it makes sense, making our app simpler. But in the case of the confirm button, I think that piece of state should ideally be isolated inside the component. In your example, the only thing I get for free when using the button component is the if-else rendering. I have to track state myself and pass it to the component. That hardly feels worth the indirection cost of using a component in the first place, I'd be better off having the full code in the parent! I'm humble to the fact that my inexperience with Choo might make me not realise best practices and the idiomatic approach, but from where I stand now I see definite value in being able to package a fully self-contained button component, where the only thing you need to do is to tell it what event to fire upon confirmation and then listen for that event. Or perhaps the value of having a centralised model trumps the value of isolating concerns in a component? Not an ironic question, I'm genuinely equal parts intrigued and unsure! :) |
I worked on an app that kept component state such as a We found that the bits and pieces we thought of as component state actually added up to a pretty good amount, and usually that component state would affect how other components render. It wasn't terrible in the end, but we're working on a new version with choo that will try to store all the bits and pieces in the central place. This also gets you an easier path to start the app from a moment in time, as long as you've got a copy of that store handy, which was a requirement for us. |
@dereklieu the approach @krawaller takes here actually mounts the localized state inside of the global store - that way the logic to declare lil scraps of data (e.g. "does my specific form have input?", "what's the class on my button?") are scoped to the view, whereas stuff that must be accessed from multiple components / views is stored in separate files. Like with everything, this should be done with caution, but I reckon it def makes sense in some cases. And because the "local" scope is just a namespace on the "global" scope, there's no hidden state anywhere. Not too bad I reckon. @krawaller I def see what you're trying to do; I feel like passing const app = choo()
app.model(myView.model)
app.router((route) => [ route('/myview', myView) ])
const tree = app.start()
document.body.appendChild(tree)
/////////////////////////////////////////////////////
// my-view.js
module.exports = myView
myView.model = { /* all of my model */ }
function myView (state, prev, send) {
/* return my cool view */
} I reckon there might be cool ways of automating this snippet of boilerplate |
Haha, I was just returning here to say much what @yoshuawuyts did: I think we're missing the point when comparing to React. Yes, the React version of the confirm button component is stateful and breaks hot reloading and time travelling. But! In the Choo version, even though isolate the definition of the relevant model parts to the component, the actual state is still stored centrally. If you were to inspect the app state at any point, the In essence - Choo is stupidly smart. Put that on the box! I agree that putting the model definition on the exported function is cleaner than passing app to the constructor, good call. |
@yoshuawuyts yeah no question, namespacing store properties within a central store is a very smart way to go about this. 👍 |
Ah, I see what you mean @krawaller - I actually faced a similar dilemma recently when building an editable table component. It lets you select one row at a time, and I wanted a way for that "state" to be localized to the component. I considered using a plain variable but then you lose the benefits described above and break the pattern. I ended with the approach I described above, where it just receives the selected row index as a property, and the consumer of the component can store/update the property however they like. The only thing I'm a bit torn about having a model coupled with the component is that it becomes choo-specific, don't you think? (Right now it uses html/bel and just returns a DOM element, which could theoretically be used without choo) |
@timwis yeah, you're totally right - in this case using the |
Closing because |
When bootstrapping an app in version
2.3.1
we did this:...where
Submission
is a component that also made anapp.model
call. This no longer worked in v3, and had to be changed toI'm guessing somehow the new architecture required the
app.model
calls to have been made before the routing. If this is really so, perhaps it should be documented?The text was updated successfully, but these errors were encountered: