Callback after store updation on child component? - react-native

In my App, store gets updated whenever an event is dispatched. But the problem is I have a child component.
Store:
state.name = 'Jack'; //initial state value
Child Component:
this.props.updateName('Micheal');
this.printName();
printName() {
console.log(this.props.name); // 'Jack'
}
After a while I call the same function.
this.printName(); // 'Micheal'
My question is that, is there any way to callback a function in child component when store is updated?

The child component gets the new state via props. So, the componentWillReceiveProps will do the trick.
componentWillReceiveProps(nextProps) {
if (nextProps.name !== this.props.name) {
console.log(this.props.name, nextProps.name);
}
}

Related

How to make vuex actions wait until a specific action completed to define state

Here's my vuex state:
state: { id: null }
Actions:
actions: {
async getId({ state }) {
const id = await api.getId()
commit('setId', id)
}
async getById({ state }) {
return await api.get(id: state.id)
}
}
I have a mounted() in my main App.vue file:
// App.vue (parent)
async mounted() {
// set 'id' globally
await this.$store.dispatch('getId')
}
The problem begins here that I also have another mounted() in one of my child views/components:
// Component.vue (child)
async mounted() {
await this.$store.dispatch('getById', this.$store.state.id) // <-- notice this
}
In the last code, I use this.$store.state.id which is null when at the run time. It's because child components created before the parent (App.vue). How do you solve such problems? I chould check if the id is null before running getById in the mounted. But mounted is called only once when the component is created so if the id is null at that time, it'll never dispatch the getById...
Please note that this is just an example code. I tried to illustrate the problem with that code.
If the mounted function is relying on a value that is not set, you can prevent the component from mounting in the first place. You can do this by making the rendering of your child component conditional.
Example:
<template>
<my-child-component v-if="$store.state.id !== null"/>
<my-loading-spinner v-else />
</template>
Usually I implement something a little more complex which watches the state of the API calls (for error handling) and use computed values such as isLoaded instead of in-lining the validation in template (for readability)

How to pass props to child component after the parent finshed the ajax call?

I have a parent component that once mounted() makes and ajax call updates some of it's DOM and passes some data to one of it's child components, all the data from the ajax response.
The child also has a ajax call on mounted() and it sends along the prop it receives from the parent,
The problem is that that the child component fires the ajax call before the parent, how can mount the child after the parent gets the data from ajax ?
Use a v-if in the parent to delay the rendering of the child until the data is ready:
Parent
<child v-if="parentData" :prop="parentData"></child>
data() {
return {
parentData: null
}
},
async mounted() {
const response = await axios.get(...);
this.parentData = response.data;
}

Vuex: How to replace function emitting between parent and child with Vuex?

I am using Vuex for state right now and taking advantage of getters to acquire state so that I don't have to use props. However, I am curious if I can use Vuex to replace this type of function emitting. How would that be done with Vuex, if it's even possible.
Parent
<child-component #handleselectproduct="selectProduct"></child-component>
selectProduct: function() {
axios.get()
}
Child
<button #click="selectProduct></button>
selectProduct: function() {
this.$emit('handleselectproductselection');
}
You could use vuex actions and mutations. Mutations are used for synchronous and actions for asynchronous calls. You could imagine them as setters as opposed to getters that you already use. So in your current example, you would call an action which may or may not set a state property through a mutation. you would define this action with:
const store = new Vuex.Store({
state: {
selectedProduct: {}
},
getters: {
getSelectedProduct: state => {
return state.selectedProduct
}
},
mutations: {
selectProduct(state, payload) {
state.selectedProduct = payload
}
},
actions: {
async selectProduct(context, axios) {
const { commit } = context
const product = await axios.get(...) // some call
commit('selectProduct', product)
}
}
})
After defining these, you can call the action through the this.$store.dispatch('selectProduct', axios) method in the child component and have your result available in the parent component or wherever else you may need it. And if you need some sort of notification that the property has changed (you need to do some change to the selectedProduct data and then show it), you can set a watcher function on the respective getter or just use computed properties that use the getter and they will pick up the change.
You can find out more about actions at https://vuex.vuejs.org/guide/actions.html

vue after $emit callback finished

In the compoent ,when call $emit('callback', params) finished, I need the returned value. Someone can help?
vueComponent:
methods: {
test: function () {
if(this.$emit('cb', param1)){
// this not working
console.log('return true')
}else{
console.log('return false')
}
}
}
vueRoot:
methods: {
cb: function () {
return true;
}
}
As per my comment below the original question, $emit only tells the parent component that an event has occurred and allows it to do something in response to the event and any data sent with it. The child component has no way of knowing what the results of the parent's actions are. In order to tell the child component something after the callback finishes, you will need to send that value through a prop and have the child watch for any changes to the prop's value.

MobX - observable not updating passed props?

When using MobX with React, I have 2 components. From the parent I send a prop to the child component like this:
import { computed } from 'mobx'
import { observer } from 'mobx-react'
#observer
class Parent extends React.Component {
#computed get user() {
const { gamer } = this.props;
}
render () {
return <div><Child user={this.user} /></div>
}
}
Child component:
import { observable } from 'mobx'
import { observer } from 'mobx-react'
#observer
class Child extends React.Component {
#observable owner = this.props.user;
render () {
return <div>{this.owner.name}</div>
}
}
The first time I run this with userX passed, the child shows the correct userX owner name, accessed via the #observable owner. The issue is the second time I run this with a different user passed userY, the child still shows userX even though the prop passed to it is correctly userY when I log it.
So the passed prop is different per user (as it should be), but the observable stays "locked" on the first user that was passed. Any idea why the observable isn't updating its value to the passed this.props.user?
Update:
So I tried #computed like this:
#computed get owner() {
return this.props.user;
}
but still the same issue. The only way I can seem to access the correct user, only in the render statement and directly from the passed prop as opposed to having mobx assign the prop value and read it from mobx observable/computed:
render() {
console.log(this.owner.name); // shows old data (even w/ observable or computed returning the passed prop)
console.log(this.props.user.name); // shows new data correctly without mobx
I just don't understand why the #observable or #computed don't return the correct new data. Is there anyway to have mobx correctly return the latest passed prop so the first console log works?
I think that we you do #observable owner = this.props.user, you do not create a reference to the original observable, but rather you create a new observable whose initial value will be the same as of the orginal one.
The solution (as it seems you already found) is to use the prop value directly in the Child component:
#observer
class Child extends React.Component {
render () {
return <div>{this.props.user.name}</div>
}
}
If you don't wanna do this, perhaps you can take a look at cloning* the observable using createViewModel from the mobx-utils package:
import { computed } from 'mobx'
import { observer } from 'mobx-react'
import { createViewModel } from 'mobx-utils'
#observer
class Child extends React.Component {
owner = createViewModel(this.props.user);
render () {
return <div>{this.owner.name}</div>
}
}
* Note: well, it is not exactly cloning, but the changes to user will get reflected in the owner object as well. Check the docs for more info.