I've seen this blog post claim that using properties to pass callbacks is an anti-pattern in Vue.js
But it doesn't make sense to me. The problems mentioned:
By passing functions down as props, you are linking both parent child components together with two-way data binding
These events are mechanically doing the exact same thing as a callback prop, so this doesn't add up.
As the application grows larger in size and other developers come on board, they will look at the child component code and have to figure out which callback function prop it is and where it is from.
You can replace the word callback function prop with event and you get the same problem.
Using functions instead of string names would avoid the kebab-case issue. Also, function-props would allow typescript to verify the function signatures are compatible. I think events should be removed and replaced entirely with callback props. The only downside I see for removing the events tech is a namespacing issue for events like click that you might want as properties.
What are the design reasons for including events as a system instead of having a few magic properties? Are callback properties an anti-pattern or is it just a matter of taste?
Related
I am building an application with a lot of screens (components) each one has some methods such as save, add, delete, redo, view, and other functions. every function of them has a lot of code in it.
now I have declared these functions outside my components and call them from my methods attached to the screen component so I can access them. when I call them I pass this keyword as an argument so I can access the state and props from these functions.
I did this to be able to share this logic across the components.
now my question: Is this a best practice?
if yes what is the best way to do this
if not what should I do instead
note I am using react
You can use a Custom Hook that contains all the re-used logic you have if that what's you want.
Read more about this feature here:
https://reactjs.org/docs/hooks-custom.html
https://www.w3schools.com/react/react_customhooks.asp
I've been doing development mostly in AngularJS, recently I was looking into Vue.js and reading its guide, on one page it mentions:
By default, all props form a one-way-down binding between the child property and the parent one: when the parent property updates, it will flow down to the child, but not the other way around. This default is meant to prevent child components from accidentally mutating the parent’s state, which can make your app’s data flow harder to reason about.
This is from https://vuejs.org/guide/components.html#Prop-Binding-Types.
I'm wondering if there are any principles of when to use two way binding, and when not to?
For the case where the child component needs to manage an array variable, it seems that two way binding would make sense.
For example, say I want to make my own Vue.js or AngularJS version of http://selectize.github.io/selectize.js/.
If two way binding is used, I would just pass the parent component's array to my Vue.js or AngularJS selectize component, and let the selectize component manage it.
If two way binding is not used, it seems the alternatives would be:
a. Either the parent component would either have to manually update the array when an item is added/deleted
b. Or the parent would have a function that manually sets the array, this function is then passed to the child component
c. The child component dispatches an event which the parent component listens to, and updates its array
I think these are the alternatives? Both seem more verbose and does not appear to provide much benefit.
This is one example, but I think many components would have this issue, another example would be if I have a product-selector component, it would be convenient to just pass in an array variable into this component, and let the component manage the array to reflect the selected products.
My main questions on this are:
Is my idea of the alternatives to two way binding correct?
For the mentioned cases, is there an advantage to using one-way-down binding? (I do not see the alternatives providing much advantage to avoid "accidentally mutating the parent’s state")
If (1) and (2) are correct, what is an example where one-way-down binding, provides a clear advantage to avoid "accidentally mutating the parent’s state"?
"one-way-down binding" is just a principle, similar to OO's Encapsulation, to reduce code complexity.
In my opinion the author didn't mean not to use "two way binding" (actually vuejs support that), just mention you don't abuse it.
In your examples, the product-selector is similar to native input, surely I think you can use two way binding, just like v-model does.
I recently started trying out Ractive.js. I was particularly interested in its components. One of the things I noticed immediately is that many of the examples are using the init option. However when I try to use the init in my code I get a deprecation notice and it then recommends using onrender instead. onrender however had far fewer examples than init and some of the functions such as this.find weren't available inside onrender. I looked through the Github issues but couldn't find any reasoning behind this change or what the suggested path forward for selecting elements specific to a component was.
I created a test pen to try creating a recursive component with the new API but I had to resort to using jQuery and an undocumented fragment api to select specific DOM nodes I needed to manipulate. I feel this is against how Ractive expects you to do things, but I couldn't figure out what was expected of me with the existing documentation.
What's the major differences between the init and onrender options and how does onrender expect you to handle manipulating specific elements and their events within a component?
You can use this.find() inside onrender (if you can't for some reason, you've found a bug!).
We split init into oninit and onrender a while back for a number of reasons. Those examples you mention are out of date - are they somewhere on ractivejs.org? If so we should fix them. You can find more information about lifecycle events here on the docs, but the basic difference is this:
init was called on initial render (assuming the component was rendered, i.e. never in node.js, if you were doing server-side rendering)
oninit is called on creation, immediately before rendering. It is called once and only once for any Ractive instance, whether or not it gets rendered. It's therefore a good place to set up event handlers etc. The opposite of oninit is onteardown, so you can use that handler to do any cleanup if necessary (or use this.on('teardown'...) inside oninit).
onrender is called whenever a component is rendered. This could happen more than once (if you unrendered, then re-rendered, etc) or not at all. If you need to store DOM references etc, this is the place. The opposite of onrender is onunrender.
I did a fork of your codepen, replacing the jQuery stuff with more idiomatic Ractive code to show how you'd do it without storing DOM references.
Is there a way in ReactJS for a component to find out who it's parent is?
EDIT 1: Regardless of the merits of doing this, is there a way?
I haven't found a React way to do this - from what I can see, the idea is to pass callbacks down to the child from the parent, and the child calls the callback - unaware of the fact that the callback is actually on the parent.
I've tried setting an "owner" property, and that idea seems to work, but I wonder what's the best approach?
e.g.
<Parent>
<Child owner={this}/>
</Parent>
Then in the child component, I can do owner.method, and it seems to work fine. I know this isn't a true parent/child relationship, but is the closest I've found in my tests.
Some may say that callbacks are a cleaner way of doing this, but the parent/child relationship for some things (e.g. RadioButtonGroup and RadioButton) seems natural and would benefit from knowing this relationship, in my opinion.
EDIT 2: So it's not possible?
The thing that I don't like about the idea that it's not supported is
that HTML can be marked up with zero javascript - and it has implied,
default functionality - some elements are required to have parents -
they are defined as children of other elements (e.g. ul and li). This
can't happen in JSX because if there is interaction between the
elements - there has to be javascript events that bind the
components together - every single time you use them. Designers can't
simply write HTML like syntax - Someone has to step in and put some
javascript bindings in there - which then makes the maintenance
harder. I think the idea makes sense for overriding default behavior,
but default behaviors should be supported. And defaults would
require knowing either your parent, or who your owner is.
There are a number of benefits to not doing this, the main two are: reusability and encapsulation.
TL;DR you probably don't want to do this ever.
Let's say our RadioButton has this public interface:
<RadioButton active={true} onSelect={function(event){}}>text</RadioButton>
We could construct another component called SuperRadioButton, which might have a different way of presenting itself, but still have the same public api as RadioButton, so it's a valid child of RadioButtonGroup.
If we're accessing the parent, then the parent's internals become part of the public api of these components, and we need to be much more careful with how we change our code, because a change in any of these components could cause the entire application to break.
Callbacks. Owner properties. Passing events out to be caught by the root in the tree. Passing the root down through contexts.
There are ways, yes, but they're contrary to the conceptual model of react, which is to be explicitly top down at all times. The short version is "you can, but don't."
The fundamental problem is that you don't want a child mutating outside its parent's knowledge.
That means that the sole exception to this is the root of the component tree, so it's semi-legit to pass a member of that control downwards in props or contexts then to "pass things up" by telling the root, which may then repaint itself.
The application layer Flux does something not terribly dissimilar to this, but passes things outside of the component heirarchy entirely to a dataStore, which broadcasts things back in with events.
I've inherited a rather large Javascript/ExtJS3 code base, and there are many instances of invoking events inside of the overridden initComponent method, after the call to "...superclass.initComponent.apply(this, arguments)". Specific events are being invoked on specific objects in a manner such as the following:
this.filter.on('filterUpdated', function(filter, params)
I've started converting the code to use a pub/sub paradigm instead, to reduce the coupling between objects and their specific event names, but quickly ran into issues when publishing and/or subscribing to events within initComponent (which in ExtJS executes before rendering). I need to fire an "INIT" event from the highest level component when the screen first loads, and I was either getting an error (due to ExtJS "templates" not having been rendered as it turns out), or events not firing at all.
Then I read the following in the ExtJS source for Ext.Component (from which all components extend) and I had an "aha" moment:
if (this.tpl) {
if (!this.tpl.compile) {
this.tpl = new Ext.XTemplate(this.tpl);
}
if (this.data) {
this.tpl[this.tplWriteMode](contentTarget, this.data);
delete this.data;
}
}
this.afterRender(this.container);
When I switched to both publishing the "INIT" event from my topmost component's afterRender method, and subscribing to all events from all other components from their afterRender methods, everything worked as I expected. And now I am just wondering, largely to validate my design....
Is this a generally accepted way of implementing pub/sub in an event driven UI? Regardless of framework even? Namely are the following 2 good principles, or are their other ways?
"Initialization events" should get published after all sub-components have rendered
All sub-components should subscribe to all events (to be on the safe side) after they have rendered
Thanks in advance
You have to balance the overhead of event handling vs. the possibility of missing significant events. In js/DOM land state is mutable.
For your #1 if you can identify a point in time when all your sub-components have rendered and subscribed, firing an init event makes sense.
For #2, it seems safe everyone to listen for events; however it could slow things down. If performance issues are apparent you may have to decide what events you don't care about and avoid subscribing.