-
Notifications
You must be signed in to change notification settings - Fork 59
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
Writing Maintainable Components, Best Practices. #131
Comments
You have a good point. I think that for indicating which state properties are private vs public using underscore, like you mentioned, should be enough. That's what I've been using so far and at least to me this is enough for that purpose. Not sure what should be done for properties coming from the store though. I didn't find anywhere showing best practices for this when using react-redux, the examples I see don't try to distinguish regular props from store ones. Do you have an example of code doing something like this so we can have an idea? From what I understood actually, an advantage of So having that in mind I think that a separation via a I'd also want to learn other people's opinions on this, this is a good discussion to have, so let's make use of this opportunity :) |
Naming convention for private/public methods with an underscore is definitely useful, but I still think there is something to be said about having an api distinction between public and private state. The big question for metal right now is "what is best practice and implementation?" Component life cycle is very similar to react and fairly easy to pick up, however the flow of data can be confusing as to what is the best approach or the "metal way" of doing things. Just taking an example from the metal docs, there are multiple ways to respond to data changes both within the component and also from the parent component. Take a look at just a few examples below of handling data-flow in metal var obj = new Calculator();
obj.on('numberChanged', function(event) {
// event.prevVal has the previous value.
// event.newVal has the new value.
}); class Parent extends Component {
onChange(newNumber) {
this.number = newNumber;
}
render(newVal) {
return <Calculator number={this.number} onChange={this.onChange} />;
}
}
class Calculator extends Component {
onChange_() {
this.config.onChange(this.number + 1)
}
} class Calculator extends Component {
syncNumber(newVal) {
// respond to change
// Could potentially emit this.config.onChange that is passed from parent
}
} So you can see, from a developer who is try to use metal in their app, it can be really confusing as to which approach to choose. And sometimes having so many different options can be more of a headache to navigate. React was explicit in their "one-way data flow". A component would never alter its My thought for metal is that there needs to be some sort of best practice or guidelines of how data flow is supposed to be handled. It seems like there are 10 different ways to change state for a single component. I'm not saying we need to copy React and provide |
Before you guys started using it we didn't even have After requests, we agreed that having We also have the events Also, is important to pay attention and not over-engineer the flow based on all different theories put on the table by those new frameworks, where in reality the problem is simple if taken correctly. I think it maybe time for us to do a meeting to explain the parts you described is getting messy. Either this may be an alert for us to dry down our api or we can just point to the best practice in case they fill the gap you are having. |
Just my 2 cents: Personally, I think the uni-directional data-flow is the easiest to go towards for end developers who are building components. var SRC = {src: 'ui'};
A.Component.create({
....
bindUI: function(){
this.on('fooChange', this._afterFooChange);
}
someMethod: function(){
this.set('foo', SRC);
}
_afterFooChange: function(e){
if (e.src !== SRC) {
// respond to user modification
}
}
}); So I can definitely see some benefits to allowing a single direction of data flow, but of course, there may be other approaches where you guys can think of that would be better. |
Thanks for all the feedback everyone! This was really helpful. Me and Eduardo also had a meeting with some of you last Friday, but I forgot to document here the conclusions we came up with then. We all agree that having a way to distinguish public/internal data is important, as not having it is causing many of the current problems related to best practice confusion as well as some technical problems too. On Friday we've agreed to offer this distinction as well as different namespaces where they can be accessed ( Me and Eduardo realized that this namespace separation won't be so good for soy though. That would make passing data down to sub components a lot harder inside soy templates for example, besides other problems. The template you use ends up affecting the way you use the component, and specially the component's data, which is why we've had to change many things recently related to JSX usage which aren't issues with Soy. So we've decided to build a way to handle component data differently for different templates too. By default we'll keep our current behavior, of having all data be accessed directly via the component instance (though now you'll be able to specify which are internal only). For JSX components data will be automatically split between All this (plus the change in the approach of reusing components, explained here) will require some bigger changes than usual though. It'll be risky to publish it by just incrementing rc again. This version of Metal.js is quite stable though, so we've decided to go ahead and finally release a 1.0.0 version with the current features, so that these changes (which I'm already working on, and will mainly just affect JSX usage) can be released as a 2.0.0. |
This issue was moved to #3 |
This issue was moved back. |
The changes we've agreed on here are now available on version 2.0.0. I'll be updating our other repos that will be affected by these changes (such as metal-redux and metal-react) to use the new version soon. |
@mairatma, with the new API changes in 2.0.0 were you going to add the sugar-api to reduce the boilerplate or was that something you were expecting projects to do on an individual basis? I remember talking it about it in one of our meetings, but wasn't sure what the expectation was. We can also open a different issue and talk about there if that is easier. |
Yes, that's also on the roadmap, just hasn't been done yet. I'll open another issue just for this feature so we can track that better :) |
I've created #136 for this. Check it out when you can, I have some proposals there and would like to know which one people like best :) |
perfect. Thanks! |
I'm wondering if we should define a set of guidelines for writing maintainable components. Metal gives the developer a lot of freedom when it comes to how they can organize their components, but this comes at a cost.
I know it has been brought up before, but React has some simple ways that it encourages you to write reusable components. Things like the separation of
prop
,state
, andpropTypes
(and now flow) are examples of this.propTypes
was the inspiration formetal-state-validators
, which is useful because it both documents your component, and also gives you useful warnings.Of course you don't have to write code using the "one-way data flow" or React mindset, and I'd like to hear other's thoughts on how to architecture large applications in Metal.
As an example, here is an example (somewhat poor on purpose) of how things can become messy:
If I wanted to use this
User
component, how do I know what I need to pass to it? It's not enough to just look at theSTATE
config. I need to cross reference it with mymapStateToConfig
,mapDispatchToConfig
, and also determine which of theSTATE
attributes are managed internally by the component. Realistically, I could probably do a better job here, like prefixing the private state variables with an underscore, or maybe naming my action creators differently (pingAction
instead of justping
). However these are best practices that need to be defined, and that's what I am trying to bring up here.Good naming schemes are nice, but if something is important enough, it may be something that should be easy to enforce through the framework's API.
For example, here is an imaginary Metal-like API for the same component:
This isn't a proposal for any API changes, I just wanted to think about how best practices might influence API changes in Metal down the road.
Thanks, and I am very interested to hear what other people or teams are doing when it comes to this!
\cc @bryceosterhaus, @ethib137
The text was updated successfully, but these errors were encountered: