In my app.component.html I create a custom component (contact-table component) that should get a new value (Account) to present after I update the in the store the connected user to be someone else.
app.component.html:
<contact-table [contacts]="connectedAccount$ |async"></contact-table>
app.component.ts:
export class AppComponent implements OnInit
{
private connectedAccount$: Observable<Account>;
constructor(private store:Store<any>) {
this.connectedAccount$ = this.store
.select(state=> state.connectedAccountReducer.connectedAccount);
this.connectedAccount$
.subscribe(account1=> {
//the app prints any new account that comes up. It's working here.
console.log(account1);
});
}
public ngOnInit() {
this.store.dispatch({
type: updateConnectedAccount,
payload: {
connectedAccount: ACCOUNTS[0]
}
});
}
}
The subscribtion in AppComponent class works great and fires any update that comes up.
The problem is that the async pipe in app.component.html does not send the new account to the contact-table component. I think he doesn't get notified for any updates.
I tried to see what is being sent to the contact-table component,
and I saw he only get one value in the first creation of the contact-table component and its undefined. Thats not possibole becouse my reducer function creates an empty Account object for the initial state of my app.
Is there anything you notice that I missed?
Check and make sure you are using Object.assign to create the changes to your state. It shouldn't return a mutated state. Redux/ngrx and the async pipe detect changes when the object hash changes (or at least this is my understanding). I've run into this problem when I wasn't creating a brand new object but accidentally mutating the existing state and returning it.
To quote the redux site -> http://redux.js.org/docs/basics/Reducers.html
We don’t mutate the state. We create a copy with Object.assign(). Object.assign(state, { visibilityFilter: action.filter }) is also wrong: it will mutate the first argument. You must supply an empty object as the first parameter. You can also enable the object spread operator proposal to write { >...state, ...newState } instead.
I also had a async issue with RC2 so if you aren't using RC3 or above I'd recommend upgrading.
Not saying this is it but it is the most likely candidate from my experience. Hope this helps.
Try using the json pipe just to see if the view is getting anything from that store select.
<span>{{connectedAccount$ |async | json}}</span>
You can include ngrx-store-freeze - the problem with mutable state is easy to address.
Another way of how I often debug the dispatching of Actions is to introduce an Effect for logging purpose. This helps to identify problems as well.
Related
I'm currently building a Vue app that consumes data from the Contentful API. For each entry, I have a thumbnail (image) field from which I'd like to extract the prominent colours as hex values and store them in the state to be used elsewhere in the app.
Using a Vuex action (getAllProjects) to query the API, run Vibrant (node-vibrant) and commit the response to the state.
async getAllProjects({ commit }) {
let {
fields: { order: order }
} = await api.getEntry("entry");
let projects = order;
projects.forEach(p =>
Vibrant.from(`https:${p.fields.thumbnail.fields.file.url}`)
.getPalette()
.then(palette => (p.fields.accent = palette.Vibrant.hex))
);
console.log(projects);
// Commit to state
commit("setAllProjects", projects);
}
When I log the contents of projects right before I call commmit, I can see the hex values I'm after are added under the accent key. However, when I inspect the mutation payload in devtools, the accent key is missing, and so doesn't end up in the state.
How do I structure these tasks so that commit only fires after the API call and Vibrant have run in sequence?
You cannot add a property to an object in Vue and have it be reactive; you must use the Vue.set method.
Please try replacing that forEach block with the following, which adds the new property using Vue.set:
for (i=0; i<projects.length; i++)
Vibrant.from(`https:${projects[i].fields.thumbnail.fields.file.url}`)
.getPalette()
.then(palette => (Vue.set(projects[i].fields, accent, palette.Vibrant.hex)))
);
UPDATE: changing the format from forEach to a conventional for loop may be gratuitous in this case, since the assignment being made is to an object property of projects and not to a primitive.
I'm not spending a lot of time on StackOverflow, and if the above answer works, I am happy for you indeed.
But I expect from that answer you will get console warnings telling you not to mutate state directly.
Now when this happens, it's because while Vue.set(), does in fact help Vue understand reactively a change has been made, potentially deeply nested in an object.
The problem here is that since you are looping the object, changing it all the time, the commit (Mutator call) is not the one changing state - Vue.set() is actually changing it for every iteration.
i have noticed that when you use a custom component with a input property and you bind a function to it this function is called lots of times.
E.g.
<some-tag [random-input]="randomFunction()"></some-tag>
and in the class
private randomFunction() {
console.log('Called!');
return true
}
if you run something simple as this you will see in the console a few dozens of 'Called!' logs.
In my project the randomFunction makes a call to the database, so this is pretty anoying.
Does anyone knows why is this happening?
Angular runs this with every cycle, trying to check for updated value, that's why you see so many messages in the log.
For this reason it is not good practice to have ts functions as inputs to component.
You can for example make a call to the server/database in constructor, OnInit or OnChanges, store the result to local variable and make that variable as input to component. Something similar to this:
export class MyComp {
dbResult: any;
constructor(http: HttpClient) {
http.get('/my/api/call').subscribe(result => {
this.dbResult = result;
});
}
....
}
..and in HTML:
<some-tag [random-input]="dbResult"></some-tag>
As a sidenote, having that function marked as private will eventually fail during ng build --prod
Angular needs to check if the value has changes otherwise it can't update the value inside the component.
Reading through this article covering OnPush Change Detection and it features the following service that uses a BehaviorSubject to load a user:
const ANONYMOUS_USER: User = {
firstName: '',
lastName: ''
};
#Injectable()
export class UserService {
private subject = new BehaviorSubject<User>(ANONYMOUS_USER);
user$: Observable<User> = this.subject.asObservable();
loadUser(user:User) {
this.subject.next(user);
}
}
Suppose the subject reference is never updated with a new user. In other words it sticks with the ANONYMOUS_USER state that it is initialized with.
Will all components that are injected with UserService and call loadUser receive an instance of the ANONYMOUS_USER. IIUC that is the purpose of BehaviorSubject. It remembers the current state so that loadUser can be called multiple times by different components and they will always receive the ANONYMOUS_USER?
Not quite.
All components that subscribe to user$ will receive the ANONYMOUS_USER value from the subject.
So for example, if a component A subscribes, it will immediately get the ANONYMOUS_USER value. If the user navigates to another component B, the original component A is destroyed. If the user navigates back to component A and it resubscribes, it will get the ANONYMOUS_User value again.
This code:
loadUser(user:User) {
this.subject.next(user);
}
Is what broadcasts a new value into the observable stream. So if any components call this method, they will broadcast a new value ... whatever they passed in to the method.
I have a complete example of using BehaviorSubject here: https://github.com/DeborahK/MovieHunter-communication/tree/master/MH-Take3
And code that accomplishes the same task using getters/setters instead of BehaviorSubject here: https://github.com/DeborahK/MovieHunter-communication/tree/master/MH-Take4
EDIT:
The main purpose of a Subject is to broadcast events to subscribers. It allows components to call a method (loadUser in this example) and pass a value. That value is then broadcast to all subscribers.
If you are using data binding, in most cases a subject is not needed. That is because Angular's change detection will provide the notification automatically.
A BehaviorSubject is a special type of Subject that has an initial value and provides that initial value to the subscriber when they subscribe. It is often used instead of a Subject if you need the subject to "remember" its last value.
Let's assume I have an action that gets dispatched on page load, like say, in the index.js file.
example
store.dispatch(loadData());
in my reducer I initialize state to to an object. Something like this
function myReducer(state = {}, action)
now I have some smart component that subscribes to my state and then passes it down to another component to display the data. Another important note for this scenario is the fact that the retrieval of the data is happening asynchronously.
Let's also assume that the key of this object is some array.
So the markup component would have something like this
{this.props.object.key.map(k => do something)}
Now since key is undefined, if I call map on it, I blow up. The way I have been dealing with this, is by using a simple if check. If key is defined then run .map otherwise return null. Then by the time my data gets back from the server, the render will be called again due to a change in state that this component subscribed to. At this point the key is defined and map can be called.
Another approach, Is to define what your state will look like in the reducer. In other words, if I know that my state will be an object with an array property on it, I might do something like this.
const initialState = {
key:[]
}
function myReducer(state = initialState, action)
Doing this will benefit in the fact that now I won't need my if check since key is never undefined.
My questions is; are any of these approaches better than the other? Or perhaps, is there another way entirely to deal with this?
Generally, the approach I like to take is to define default props on the component which have the semantic meaning of "empty." For example, in context of the issue you describe I would typically structure my component like this (ES6 classes style):
class MyComponent extends React.Component {
static defaultProps = {
object: {
key: []
}
};
...
method() {
// this.props.object.key is an empty array, but is overriden
// with a value later after completion of the reducer
// (trigerred asynchronously)
this.props.object.key.map(doSomething);
}
}
This is relatively clean, prevents the code from throwing at run time, and forces you to create well-defined behaviors for semantically null, empty, or undefined input states.
I am kind of confused here on where to place my global functions. In a lot of examples a main.js file points to an app component and this is placed somewhere within the html. This workflow would be fine for me If I were to simply contain all my logic within this app component. But I am combining components with Laravel functionality so this does not work for me.
Currently my main.js file contains a bunch of methods that I need to have access from anywhere in my app. These methods don't contain any broadcasting events so they can effectively be placed anywhere as long as they get a vue-resource instance.
My main.js file:
https://github.com/stephan-v/BeerQuest/blob/develop/resources/assets/js/main.js
Hopefully somebody can tell me where I could place my friendship methods if I were to use vuex or in general since this does not seem like best practice at all.
Thank you.
Vuex manages all of the data in your application. It's a "single source of truth" for data on your front-end. Therefore, anything that changes the state of your application, such as adding a friend, or denying a friend, needs to flow through Vuex. This happens through three main function types, getters, actions, and mutations.
Check out: https://github.com/vuejs/vuex/tree/master/examples/shopping-cart/vuex
Getters are used to fetch data from storage in Vuex. They are reactive to changes, meaning if Vuex data changes, the information in your component is updated as well. You can put these in something like getters.js so that you can import them in any module you need them in.
Actions are functions that you call directly, ie. acceptFriendRequest when a user clicks the button. They interact with your database, and then dispatch mutations. In this app, all of the actions are in actions.js.
So you'd call this.acceptFriendRequest(recipient) in your component. This would tell your database to update the friend status, then you get a confirmation back that this happened. That's when you dispatch a mutation that updates the current users' list of friends within Vuex.
A mutation updates the data in Vuex to reflect the new state. When this happens, any data you are retrieving in a getter is updated as well. Here is an example of the entire flow:
import {addFriend} from './actions.js';
import {friends} from './getters.js';
new Vue({
vuex:{
getters:{
friends
}
},
methods:{
addFriend
}
}
store.js:
export default {
state:{
friends: []
},
mutations:{
ADD_FRIEND(state, friend) {
state.friends.push(friend);
}
}
}
actions.js:
export default {
addFriend(friend){
Vue.http.post('/users/1/friends',friend)
.then((response)=>{
dispatch("ADD_FRIEND", response) //response is the new friend
})
}
}
getters.js
export default {
friends(state) {
return state.friends;
}
}
So all of these are organized into their own files, and you can import them in any component you need. You can call this.addFriend(friend) from any component, and then your getter which is accessed from this.friends will automatically update with the new friend when the mutation happens. You can always use the same data in any view in your app and know that it is current with your database.
Some misc stuff:
getters automatically receive state as a variable, so you can always reference the state of your Vuex store
mutations should never be asynchronous. Do fetching/updating in actions and then dispatch mutations just to update your data
creating services (or resources) using Vue Resource will make fetching/updating/deleting resources even easier. you can put these in separate files and import them in your actions.js to keep the database retrieval logic separated. Then you'd be writing something like FriendService.get({id: 1}) instead of Vue.http.get('/users/1'). see https://github.com/vuejs/vue-resource/blob/master/docs/resource.md
Vuex works with vue devtools for "time-traveling". You can see a list of every mutation that has taken place and rewind them/redo them. It's great for debugging and seeing where data is being changed.