Run code only when all bindings are ready - javascript

So I'm writing this Aurelia application and one thing annoys me a lot. Say I have a custom component defined like this:
export class CustomComponent {
#bindable callbackForSomething;
#bindable anotherCallback;
}
Now, I have a couple of cases where I have to bind even more functions (or just anything else) on my component. So in each component I have code like this:
callbackForSomethingChanged() {
this._tryRunComponent();
}
anotherCallbackChanged() {
this._tryRunComponent();
}
_tryRunComponent() {
if (!this.callbackForSomething || !this.anotherCallback) {
return;
}
// run some logic here when I know the component is ready
}
Does AureliaJS have something to make this easier? With only two properties it's annoying, but I have components declaring a lot more properties.

Add a bind method to your view-model. It will be invoked by Aurelia once all of the bindable properties have been assigned.
Subsequent changes to the bindable properties will trigger your *Changed methods.

Related

Class properties for react lifecycle methods

Can I write React lifecycle methods as class properties?
I've been using class properties for a while as I like the fact that I no longer have to manually bind my methods, but I'd like to keep some consistency across my components and I'm wondering if there is any drawback on writing the React lifecycle methods as class properties
import React, { Component } from 'react';
class MyComponent extends Component {
render = () => {
return (
<div>Foo Bar</div>
);
}
}
export default MyComponent;
For example, is the context of this class property affected compared to the context in an equivalent method. Given that the render method in the above code is written as an arrow function, this concern seems relevant.
In a way, the true answer depends on your build pipeline and what the resulting Javascript output looks like. There are two primary possibilities:
Input Code
Let's start by saying you are writing the following before going through any sort of pipeline transformations (babel, typescript, etc):
class Test {
test = () => { console.log('test'); };
}
Output as class member variable.
In one possible world, your pipeline will also be outputting the test function as a member variable for the output class. In this case the output might look something like:
function Test() {
this.test = function() { console.log('test'); };
}
This means that whenever you write new Test() the test function is going to be recreated every single time.
Output as class prototype function
In the other major possibility, your pipeline could be recognizing this as a function property and escape it from the class instance to the prototype. In this case the output might look something like:
function Test() {
}
Test.prototype = {
test: function() { console.log('test'); }
}
This means that no matter how many times you call new Test() there will still be only one creation of the test function around in memory.
Desired behavior
Hopefully it's clear that you want your end result to have the function end up on the prototype object rather than being recreated on each class instance.
However, while you would want the function to not end up as a property, that doesn't necessarily mean you couldn't write it that way in your own code. As long as your build chain is making the correct transformations, you can write it any way you prefer.
Although, looking at the default babel settings (which your babeljs tag leads me to believe you are using) it does not make this transformation for you. You can see this in action here. On the left I've created one class with the function as a property and one class with the function as a class method. On the right hand side, where babel shows it's output, you can see that the class with the function as a property still has it being an instance-level property, meaning it will be recreated each time that class's constructor is called.
I did find this babel plugin, which seems like it might add this transformation in, but I've not used it for myself so I'm not positive.
In my experience, the most reason for writing a method as a class property is when the method will be passed as a callback, and you need it to always be bound to the instance. React lifecycle methods will always be called as a method, so there's no reason to bind them (and you incur a tiny memory penalty when you do). Where this makes a difference is when you're passing a function to a component as a callback (e.g. onClick or onChange).
Take this example:
class BrokenFoo extends React.Component {
handleClick() {
alert(this.props.message);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
)
}
}
The function represented by this.handleClick is not automatically bound to the component instance, so when the method tries to read the value of this.props it will throw a TypeError because this is not defined. Read this article if you're not familiar with this; the problem described in section 4.2 "Pitfall: extracting methods improperly" is essentially what's happening when you pass around a method without making sure it's bound correctly.
Here's the class, rewritten with the handler as a class property:
class HappyFoo extends React.Component {
handleClick = () => {
alert(this.props.message);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
)
}
}
Effectively, you can think of the handleClick definition in the second example as placing this code into the component's constructor (which is just about exactly the way Babel does it):
this.handleClick = () => {
alert(this.props.message);
}
This achieves the same thing as calling bind on the function (as described in the linked article) but does it a little differently. Because this function is defined in the constructor, the value of this in this.props.message is bound to the containing instance. What this means is that the function is now independent of the calling context; you can pass it around and it won't break.
The rule of thumb that I follow: by default, write methods as methods. This attaches the method to the prototype and will usually behave the way you'd expect. However, if the method is ever written without parentheses (i.e. you're passing the value and not calling it), then you likely want to make it a class property.

Using method on class or function in closure

Say I want to create a Simple React component with some methods (no state or life-cycle methods). I can do it one of two ways:
// Foo.js
class Foo extends React.Component {
doSomething() {
// do something
}
render() {
...
this.doSomething();
...
}
}
export default Foo;
Or I can do it like this:
// Foo.js
const doSomething = () => {
// do something...
}
const Foo = () => {
...
doSomething();
...
}
export default Foo;
The second is preferable because it's a functional component (simpler, less overhead), but does the closure function come with any overhead / risk of memory leaks?
In general, if a component doesn't keep state or use life-cycle methods, which is the preferable way to implement the component?
Generally, if you:
Do not want to maintain state
Don't need to use lifecycle methods ( like componentDidMount )
it's better to use stateless functional components, which are fast and more readable. And no, you needn't worry about memory leaks due to closure. For more benefits of stateless components, see https://hackernoon.com/react-stateless-functional-components-nine-wins-you-might-have-overlooked-997b0d933dbc
There is no need to use class-based components if you are not keeping track of application state or life-cycle methods simply because it is a lot of overhead, and you don't need to keep track of the this keyword, which is frankly, annoying.
A functional component is dumb. It has no idea of the application state at all. It can be used to display / present the component without having to worry about updating itself when an event is emitted.
In your case, if the doSomething() method does not involve any dynamic behavior, it is preferable to go with the second approach.

Angular 2 Component Factory Resolver Helper Function

I have been working with the Component Factory Resolver for awhile and while I think it's pretty slick, there is one thing that drives me nuts. I would love to wrap most of the repeated code into a helper function that renders the component.
In my case I have a dashboard component where we render quite a few different components by altering singleton services to trigger visibility or not. Rather than having a ton of these create component code blocks, I was wondering if anyone has successfully create a helper-like function that a few variables can be passed into to achieve the same effect, thus eliminating a lot of the repetitive code.
Below is my attempt at a helper function and the call to activate it. The component gets created, but the destroy function doesn't work. I have narrowed it down to the Component Reference not actually being saved to the globally accessible component. Is there a way to store component references within a global array? If so how would you go about dynamically accessing them as components are added/destroyed?
Subscription within ngOnInit
// Subscribe to Create User Modal Visibility
this._ComponentVisibilityService.createUserVisibility$.subscribe(
_createUserVisibility => {
this.renderComponent(
this.createUserModal,
CreateUserComponent,
this.createUserModalContainer,
_createUserVisibility
)
}
)
Function within the dashboard component
renderComponent(component, template, container, visibility) {
if (visibility) {
// Destroy previously built components if not already destroyed
if (component) component.destroy();
// Generate component factory
const componentFactory = this._ComponentFactoryResolver.resolveComponentFactory(template);
// Render the component
component = container.createComponent(componentFactory);
} else {
// Destroy the component if visibility is false
if (component) component.destroy()
}
}
So ended up doing some digging last night and found that Typescript considers any to be a basic (primitive) type. So from what I can tell, none of the methods, or structure are available to component, unless altered to not fit into the "basic" category, otherwise its passed as a value and not a reference. However, like in javascript, an object is not considered a primitive type so I refactored the component variable to be cast as an object with a property component as ComponentRef<any>, and it worked!
Component Property Declaration
createUserModal: { component: ComponentRef<any> } = { component: null }
Function Declaration
renderModal(component: { component: ComponentRef<any> }, template, container, visibility) {
// Create Component...
}
This combination allows the object to passed in as a reference which in turn allows the function to alter the value of DashboardComponent property this.createUserModal, which in turn allows all of the methods to be called on it as normal.

Is it possible to create a global attach() handler for viewmodels?

I have a use case with Aurelia where I would like to have a handler run for every view that is attached. (It's an HTML5 polyfill for date and number inputs that would work via querySelector.) I realize that I could call this within every view that I create, but I'm wondering if there's a best practice to set this at a global level. (Note: This could probably be done with a router pipeline step, but all views may not be subject to that, such as views loaded via compose.)
I realize that this could potentially be dangerous, but is there a best practice to add global attached() and detached() handlers for views and viewmodels?
Edit: Looking here (https://github.com/aurelia/templating/blob/ee5b9d6742fddf3d163aee8face6e6a58ba1554c/src/view.js#L259) it looks as though it would be possible to add a hook for a global handler here that took a view as an argument, but I'd rather not have to change the framework code if possible.
My idea would be to create a base viewmodel class with an attached logic, which would contain globally required functionality.
Extended viewmodels could call super.attached() to execute global logic as needed.
You can find a demo here: https://gist.run/?id=fea4069d8a4361c4802c7c5d42105145
This can work with compose as well. I know, it isn't a completely automated solution but an opt-in method, so it would require a bit of additional work on all viewmodels.
Base class - used by all viewmodels
import { inject } from 'aurelia-framework';
#inject(Element)
export class BaseView {
constructor(element) {
this.element = element;
}
attached() {
// global logic goes here
}
}
Example viewmodel - actual implementation
import { BaseView } from './base-view';
import { inject } from 'aurelia-framework';
#inject(Element)
export class ExtendedView extends BaseView {
constructor(element) {
super(element);
}
attached() {
super.attached(); // global logic runs
}
}

Mixins and duplicate methods in React.js

Getting more and more into the awesomeness that is React.js and I've started to use Mixins more.
One thing I noticed, is that both my mixin and my component can have a componentDidMount method — And both functions will be called, so defining it in the component won't override the one in the mixin and vice versa.
Here's an example:
var MyMixin = {
componentDidMount: function() {
// Do something when component is mounted
console.log("Mixin fn ran");
}
};
var Component = React.createClass({
mixins: [MyMixin],
componentDidMount: function() {
// Do something when component is mounted
console.log("Component fn ran");
}
});
Now, the question is wether this is by design or just a coincidence that this works. The lifecycle methods are very useful (To bind and unbind events for instance), so it's not uncommon that both my component and mixins will want to rely on those. The documentation doesn't say anything about this, and I'm wanting to know if I'm setting myself up for a bad time down the road by doing this.
Another question is do I have some kind of control over which method is called first? The one in the mixin or the one in the component.
Yes, it's intentional, and the main factor that makes mixins very powerful in React.
So what happens is:
for the 'component...' functions, they're called in the order of mixin[0], mixins[1], ..., component
propTypes, and the return value of getInitialState and getDefaultProps are merged
other conflicting method names, or conflicts while merging the above results in an error

Categories

Resources