Vue expose a method from child component to parent component - javascript

Among the approaches below, which do you think is better practice ??
[ 1 ] Using $emit to expose methods from child component to parent component
$emit('updateAPI', exposeAPI({ childMethod: this.childMethod }))
OR
[ 2 ] Using $refs from parent component to access child component methods
this.$refs.childComponent.childMethod()

About $refs, according to the docs:
"$refs are only populated after the component has been rendered, and
they are not reactive. It is only meant as an escape hatch for direct
child manipulation - you should avoid accessing $refs from within
templates or computed properties."
About callbacks, I have no information about cons and there is a nice example in script section of this component of Quasar Framework, which parent component recieves via emit a function called reset and can dispatch this child function. That's why I think this way is preferable.

Related

Is the react state meant to be used within the component it is defined?

Should react component state is meant to be used in the component in which it is defined?
I faced a scenario, where a component state is updated by two different components and is passed as a prop to its child. But never used in the component where it is defined.
For example: I have a component CommonComponent which has a state, 'stateObj' and it has two child components ChildComponent and ModifyComponent.
I have one more component, CreateComponent which is a parent of CommonComponent.
I have two cases here:
During create action, CommonComponent receives props from CreateComponent and updates the state- 'stateObj' and is passed as a prop to ChildComponent
During modify action, ModifyComponent updates the state of CommonComponent using a callback and in turn the 'stateObj'
is passed as a prop to ChildComponent
Is this a valid way of using the component's state? As I understood, The state is meant to be used by its component in which it is defined. But, here I am not using the 'stateObj' in CommonComponent. but, i am just using it to send data to its child components. Am I using the state in a right way? or is there any other way of doing this?
your suggestions are really precious!
Thanks in advance.
You could move the state to CreateComponent instead of having it in CommonComponent. So, ModifyComponent and CommonComponent will have callback props which will update the state in CreateComponent.

Vue: Reasons to use props instead of referencing parent data?

In VueJS, I have seen different ways of accessing parent properties from a component. Say I want to use the parent property items in my component.
First way
The component has a props value bound to a parent property:
.js
Vue.component("example", {
template: "<div></div>",
props: ["myItems"]
});
.html
<example v-bind:my-items="items"></example>
Second Way
The child component accesses a parent's properties directly, like this:
this.$parent.items
Question
Is there a reason to use the more elaborate first method over the second? Is there an overhead to "duplicating" data like that, vs. accessing it directly when needed?
The props should be mutated in the parent component, according to the official doc :
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 prevents child components from accidentally mutating the parent’s state, which can make your app’s data flow harder to understand.
In addition, every time the parent component is updated, all props in the child component will be refreshed with the latest value. This means you should not attempt to mutate a prop inside a child component. If you do, Vue will warn you in the console
So in order to update props from child component you should use this.$emit event and send the new value in order to handle the update in the parent one.

vue, emitting vs passing function as props

Let's say I have a button component that is imported in several other components. I want the child component to not be coupled to any one type of logic that happens when the button is clicked. So I want to hold that logic in the various components that leverage this button component.
I think there are at least 2 ways of going about this.
Have the child emit an event to the parents, and then let the parents define the handler.
Define the handlers in the parents and pass it down as props to the button component.
I'm used to doing the latter in React. Is there a best practice in vue for this situation?
The Vue philosophy is props down, events up. The first option follows that closer as the event itself is emitted (up) to the parent and then handled.
Also within a Vue SFC you have the added benefit of prefixing the bound attribute with a v-on (or #) which describes its intent as an event traveling up and not a v-bind (or :) which implies it's a prop even though its really a callback to an event.
Vue.js events are callbacks, they are not DOM events. You can verify this, since you add a custom name to the event listener and not a DOM event name (click, focus...), and there is no event object passed to the function, unless you specify an $event argument in the $emit call.
Events
Pros
For libraries: keeps it lighter and clients have more flexibility on methods usage
Helpful Vue devtools event logging
Allow global listener (this.$root.on), although this can be better enhanced by Vuex.js.
Differentiated syntax: : for props and # for events/methods
Cons
Less explicit, harder to debug (fail silently if there are no listeners or the event name is misspelled)
Props
Pros
More explicit, are declarative, can be defaulted, required, validated, what turns them easier to debug (runtime errors or compilation errors in TypeScript)
Cons
Have to include props validation so you don't have to check if a function() prop exists before calling it (but using props validation is a good practice anyway...)
Conclusion
Looks like the approaches are more convention and personal preference over anything else, although I think that if it wasn't for the Vue.js documentation giving preference to the events approach, everybody would be gladly using props only, which in my opinion is better (clearer).
Props can do everything events do, except for a few cases (like $root event listening pattern - noting Vuex.js replaces this feature and is preferred for scalability), with the advantage they are more explicit, debuggable and check-prone.
Summarized from: https://forum.vuejs.org/t/events-vs-callback-props/11451
As a newbie perspective migrated from React, I don't know why #event even exists (or like the answers above - being the standard). I can't declare which events a component would $emit?, but I can easily see which props are passed down. And by a good naming, I will be able to know which one is actually a callback event.
Best Practice
Best practice would be option number 1. You can see this practice being used in the official documentation: https://v2.vuejs.org/v2/guide/components.html#Sending-Messages-to-Parents-with-Events
Performance
As long as you pass a reference to a function to be executed when using the event bus or passing down as a prop, you should see almost no performance difference.
Example using option number 1
You can use this.$emit('eventName', dataToSend, ...) to send the data to the parent component that would then listen on the component like this <my-component #eventName="yourHandler" />. You would then be able to use different logic for each button.
I have created a fiddle for a multi-select component that implements this: https://jsfiddle.net/wkdL0xbc/
// HTML
<div id="app">
<multi-choice :items="myItems" #selected="alert($event)"></multi-choice>
<multi-choice :items="myItems" #selected="sayIsCool"></multi-choice>
</div>
// JavaScript
const multiChoice = {
template: '<div class="multi-choice"><span v-for="item in items" #click="select(item)">{{ item }}</span></div>',
props: ['items'],
methods: {
select(item) {
this.$emit('selected', item);
}
}
};
new Vue({
el: "#app",
data() {
return {
myItems: [
'Homer',
'Marge',
'Bart'
],
}
},
components: {
multiChoice: multiChoice
},
methods: {
sayIsCool(item) {
alert(item + ' is cool!')
}
}
})
You’re looking for “Transparent Wrappers”
Vue's customs event works different from a native DOM event. So you need to attach .native property to the event
But if you want the event to happen on the child, then you define a computed property that will return and an object of listeners. And now you won't
By default, attributes not defined as props will be added to the root element of the view
So you can set inheritAttrs: false and then bind the $attrs to the child and it then becomes the target for those attributes
Now you don't have to think about what the root component is.
Chris Fritz does a great job explaining how they work in his 7 secret patterns talk. Starts around 21:44 https://youtu.be/7lpemgMhi0k?t=21m44s
I think this depends if we don't give a better context.
Consider props vs event is like pull vs push, quite similar to any pub-sub system.
When passing props, we inject (push) the dependencies of parent context to child context, and then child context can be polluted by parent context, not just holding the weak ref to the parent, any effect from a parent is now also executed within child context. This is also coupled between parent-child.
Consider event pulling, which parent is listening event from child, now every event data is preferably a copy value instead of ref, we don't have coupling issue between parent-child. In case we have event, we have also control by queue or custom modifier so that the usage from parent is easier to maintain (like we don't have to maintain debounce, throttle on parent context, but expect by event modifier, it should be done within child context, in this case is the Button component).

Stateless function components cannot be given refs

I try to access some refs in my component. But I have this error in the console.
withRouter.js:44 Warning: Stateless function components cannot be given refs (See ref "pseudo" in FormInputText created by RegisterForm). Attempts to access this ref will fail.
Here is my component:
class RegisterForm extends React.Component {
render() {
return (
<form action="">
<FormInputText ref="pseudo" type="text" defaultValue="pseudo"/>
<input type="button" onClick={()=>console.log(this.refs);} value="REGISTER"/>
</form>
);
}
}
Plus when I click on the button I got Object {pseudo: null}in the console. I would expect an object instead null.
I am not sure to understand why this is not working. Note that my react tree uses mobx-react.
Refs do not work with stateless components. It is explained in the docs
Because stateless functions don't have a backing instance, you can't attach a ref to a stateless function component.
Stateless components at the moment of writing actually have instances (they are wrapped into classes internally) but you can not access them because React team is going to make optimizations in the future. See https://github.com/facebook/react/issues/4936#issuecomment-179909980
You could also try using recompose it has a function called toClass.
Takes a function component and wraps it in a class. This can be used as a fallback for libraries that need to add a ref to a component, like Relay.
If the base component is already a class, it returns the given component.

Accessing Parent Properties in Nested Components

In anticipation of Routable Components coming soon, I'm attempting to use Components wherever possible in my Ember 2.0 application. I'm running into a confusing issue where I cannot access the parent component's properties from the template when provided in block form. It may be very well that this isn't possible, but wanted to be sure. Here's an example:
Template:
// templates/approvals.hbs
{{#x-secure/schedule/approvals}}
{{#x-secure/layout/full sectionTitle=sectionTitle sectionDescription=sectionDescription}}
...
{{/x-secure/layout/full}}
{{/x-secure/schedule/approvals}}
Component Template:
// templates/components/schedule/approvals.hbs
{{yield}}
Component:
// components/schedule/approvals.js
import Ember from 'ember';
export default Ember.Component.extend({
/////////////////////////////////////
// PROPERTIES
/////////////////////////////////////
sectionTitle: 'Scheduling: Approvals',
sectionDescription: 'Lots of magical , fanstastic stuff.'
});
The issue I'm having is that I'm unable to access sectionTitle and sectionDescription from the parent component (approvals) and pass it into the layout/full component. However, if I remove code from the block of the component and move it to the templates/components/schedule/approvals.hbs template, it works as expected. Is it just not possible to access a parent component's properties from the block form of a component?
Thanks!
It is not possible indeed. The component's properties are available in the component's template, but not in the template that instantiates the component.
If you need the component to make things available, it should yield them explicitly:
// templates/components/schedule/approvals.hbs
{{yield sectionTitle sectionDescription}}
And using the component:
// templates/approvals.hbs
{{#x-secure/schedule/approvals as |title description|}}
{{#x-secure/layout/full sectionTitle=title sectionDescription=description}}
...
{{/x-secure/layout/full}}
{{/x-secure/schedule/approvals}}
Notice the as |x y ...| notation to assign names to yielded values.
Anything may be yielded this way, including this (be careful with that though, that breaks encapsulation) and actions.

Categories