child component not updating when prop (boolean) is changed by parent component - javascript

When I change the props value (Boolean) in the parent Vue component, the child component won't update in order to trigger a modal to open.
In my parent component a click event is setting the value of openModal from false to true. This value is then passed through a prop down to a child component. Within that child component the updated Boolean value should then add a class through class-binding to a div which in return opens a modal.
Parent component:
<FilmListItem
v-for="slice in slices"
#click.native="openModal=true"
/>
<child-component :modal="openModal">
...
data() {
return {
openModal: false
}
}
Child component:
<div
class="modal__container"
:class="{ 'modal--show': showModal }">
...
export default {
props: {
modal: Boolean
},
data() {
return {
showModal: this.modal
}
In the vue dev tools I can see, that the value of the prop changes in the parent. Yet, my child component doesn't update. It worked when I forced the child component to reload when I was assigning a new :key value together with changing the Boolean. But that feels a little hacky to me. Also, a watcher within the child component didn't do the trick. It simply wouldn't watch the changed prop value. Any ideas very much appreciated. Thanks in advance.

I actually found the solution myself jut now:
In the child component I was trying to access the props' data through the data object. But I just accessed to props' data directly like so:
<div
:class="{ 'modal--show': modal }">
...
export default {
props: {
modal: Boolean
}
}

Correct.
Other options for syncing if this parent/child relationship is more complex, or you needed to pass back upward to the parent:
Put the :modal value in a Vuex store and use a computed property on the child component.
Use an EventBus: https://alligator.io/vuejs/global-event-bus/

Related

Sending a prop using sync and binding it using v-model in child

I'm working with BootstrapVue to get this Accordian like component to work. Part of that entails setting the visibility of a particular item.
Ie,
const obj = {
someData: [...// properties here],
visible: false
}
And this gets fed down to an Accordian component like so:
<Accordian :visible.sync="obj.visible" />
And inside Accordian:
props: {
visible: {
type: Boolean
}
}
THEN... I need to attach it to a b-collapse html tag using v-model because that is how it is suggested on their site. But that throws me this very long list of 'don't modify props directly error'
<b-collapse v-model="visibile" />
Any idea what I am doing wrong?
EDIT: And an important part I forgot to mention, the error only occurs on initial page load. Afterwards the accordian's are fine to use and work (most of the time).
You can't use v-model with a prop because props are meant to be readonly.
Instead, bind <b-collapse>.visible to the visible prop, and emit the update:visible event with the event data whenever <b-collapse>.input event occurs. The update:visible event updates the parent's visible prop via the .sync modifier.
<b-collapse :visible="visible" #input="$emit('update:visible', $event)">
demo

Getting error "avoid mutate props directly" and displaying button

i getting error message when i am passing my prop to component. This props is a to displaying button element in payment. Certain component must not show this button but when i pass the prop all the component that uses this button is hidden here are my code for the parent. My problem is same as this (https://laracasts.com/discuss/channels/vue/using-v-if-to-render-one-template-or-another-based-on-a-props) and the solution does not work for me
<payment :sharedButton="false"><payment>
this is my payment.vue button
<div class="col-lg-auto" v-if="unpaid.qr_code === 1 && sharedButton === true">
<q-btn glossy
size="md"
:label="$t('Common.GetQrCode.Button')"
#click="makePayment(unpaidIndex, true)" color="positive"
>
</q-btn>
</div>
export default{
props: {
sharedButton: {
type: Boolean,
default: true
},
here is the logic that when the sharedButton is false then hide it but when the sharedButton is true then show the button
Expected result:
Button in certain component is hidden
Current Result:
All the component with the button is hidden and get avoid mutate props directly
so i solve the my problem apparently i cannot directly assigned instead i declare a variable called isHidden in data()
data(){
isHidden : false;
}
then declare it by <payment-tab sharedButton = "isHidden">
You are facing the issue as you are directly mutating the prop in the child component and the parent isn't notified about the same. A simple way to solve this is to sync the value of prop in the child and the parent component. So, you can change the value directly in the parent component and since props are reactive the new value of the prop would directly get assigned to the child component.
I am assuming that you'd have an option to change the value back to true in your parent component.
<payment :sharedButton.sync="sharedButton"><payment>
(The second sharedButton would be the value kept in the parent component)
Now, when you want to change the prop inside the child just use
this.$emit('update:sharedButton', false)

Vue propsData not updating dynamically

I've been struggling with this issue for hours:
I've instantiated dynamically a child vue component inside a parent component, and passed in propsData some of the parent's data. The dynamically created child's properties doesn't seem to update when the parent updates.
Please check the example I created for a better understanding (forked from chinchang's example)
As you can see, I'm instantiating Button components both dynamically and statically. The color of the button depends on the type property. I pass the parent's data(the type attribute) as props both to the dynamically and statically created instances. When you insert a new button, by clicking the 'Click to insert' button, a new Button is created, using the parent's current type. After the click event, I switch the parent's type attribute. As you can see the statically created button instance (on the top on the page) changes color, but the dynamically instantiated ones remain the same.
Could you point out my mistake please and help to find a solution?
Thanks,
Ábel
From the docs:
Pass props to an instance during its creation. This is primarily
intended to make unit testing easier.
Setting propsData does not create a parent-child relationship. It simply provides (non-reactive) data to the component. In short, you've chosen a not-very-Vue approach. You shouldn't be concerned with creating components, you should have data items in your viewmodel that Vue then creates components for.
export default {
name: 'app',
components: { Button },
data(){
return {
type: 'secondary',
buttons: []
};
},
methods: {
onClick() {
this.buttons.push(true);
if(this.type === 'primary'){
this.type = 'secondary';
} else {
this.type = 'primary';
}
}
}
}
and
<div ref="container">
<button #click="onClick">Click to insert</button>
<Button v-for="b in buttons" :type="type">Click me!</Button>
</div>
Fixed demo

Accessing a component's state when render() is called

I'm using a component library and I cannot figure this out. I'm new to react and javascript and need help.
There is a component in the library that renders a header panel with tabs.
Component
|_Component.Tab
The Tab component has 2 states that change its appearance when it is clicked. But the click handler and state changes have to be defined by me outside of Tab component. How do I do this?
Seems to me by your question that you need to use props to pass the function to change state from the Component to the Tabs. Something like this:
Component
changeState(value) {
this.setState({ appearance: value });
}
render() {
return (
<div>
<Tab
appearance={this.state.appearance}
onChangeState={this.changeState}
/>
</div>
);
}
Tab
render() {
console.log('Appearance: ', this.props.appearance); // Use it for whatever you need it
return (
<div>
<Button
onClick={(value) => this.props.onChangeState(value)} />
</div>
);
}
Not sure why do you want to handle a function and it’s state outside of the component when it has to be within the Tab component. But here is the solution what you actually have to do in your Tab component to handle your state
Bind your handler function inside a constructor like below
Eg:
this.handlerFunction = this.handlerFunction.bind(this)
Call this.handlerFunction reference in your tab onClick
Eg:
onClick={this.handlerFunction}
Set state in handlerFunction
Eg:
handlerFunction(event){
this.setState({
tabClicked: event.target.value
})
}
Else I guess The outside component should be a child component that you are talking about. If so pass your tab click state as props to your outside component (i.e., child component) and receive that state as props in your child component and do setState there.
If you are still unclear then
Post your component code here. With Just theory it’s little difficult to understand the actual problem that you are talking about.

React Input Range updating state slowly

I'm trying to use an html element with the type="range" to make a range slider that will update a state value as I slide it back and forth. It works smoothly if I simply console.log e.target.value inside of onInputChange() without setting the state, but if I try to setState with the e.target.value, it lags so much that it's unusable. I've also tried setting the value={this.state.rangeValue} inside , with no success. Here is an example of the relevant code:
export default class App extends Component {
constructor() {
super();
this.onInputChange = this.onInputChange.bind(this);
this.state = {
rangeValue: 50
}
}
onInputChange(e) {
this.setState({ rangeValue: e.target.value});
}
render() {
var {rangeValue} = this.state;
// this logs unbearably slow and I want to be able to pass this value
// to a child component as a prop as it updates
console.log(rangeValue)
return (
<div className="map-controls">
<input type="range" onChange={this.onInputChange}></input>
<ExampleComponent rangeValue={this.state.rangeValue} />
</div>
);
}
}
I wonder if my package could help you out?
https://github.com/bluebill1049/react-smooth-range-input
import react from 'react';
import Slider from 'react-smooth-range-input';
export default () => <Slider value={1} min={1} max={30} onChange={/** you can put your callback here */} />;
#user3737841 It seems that you are updating the state of the component on onInputChange() handler. On each step value change of the input range slider, the setState is called and re-render happens. This is to be very costly to re-render on each input change.
If you avoid updating the state, you'll probably save some renders and your performance will improve.
You can also use defaultValue instead of value. Then the <input> is considered as uncontrolled and any user interactions are immediately reflected by element itself without invoking render function of your component.
Just replace value={this.state.rangeValue} with defaultValue={this.state.rangeValue}. Pass the slider value as prop to the <ExampleComponent /> component regrdless of component state.

Categories

Resources