part of the store relies on some other part - redux

As a scenario, the user can click a button to create a list of timestamps that shows the corresponding times when the clicks are made. User can also click on an item on the list to remove an item.
In terms of the store, there's a counter state that keeps track of how many times the button has been clicked, and then there's another state that keeps track of a list of timestamps. And each item on list state has an id field that derive from the counter state. So one part of the store depends on another part.
As an attempt, I dispatch one action, and both reducers handle the same action, and it works fine, but only that it's not DRY. Before dispatching, I have to add 1 to the counter state in order to get the new id which I use as the action payload, after dispatching, I add 1 to the counter state again to return the new counter state. That's repeating myself.
What's the general standard way of handling a problem of this nature?

The general simple way is to use thunks. You need to setup a middleware, check out the docs:
https://github.com/gaearon/redux-thunk
This allows you to dispatch a function instead of a simple action. Within that function, you can access state and dispatch as many times as you want.
In your scenario, you would first increment the counter, then retrieve the length to get your new id, and then dispatch another action to create a timestamp.
Some imaginary code for your action creators:
// basic action creators to be handled in your reducers
function incrementCounter(){
return { type: 'INCREMENT'}
}
function createTimestamp(id){
return { type: 'CREATE_TS', id }
}
// this is the thunk
function incrementAndTimestamp(){
return (dispatch, getState) => {
// increment the counter
dispatch(incrementCounter())
// generate an "id" from the resulting state
const newId = getState().counter.length
// and use that id to further update your state
dispatch(createTimestamp(newId))
}
}
You will need to handle those 2 different actions in your reducers and you have now two separate pieces of code. The thunk is the glue that dispatches, gets the data from one part, and uses it to affect the other part.

Related

Dependency and synchronization in Ngrx effect (API) calls

My component needs data from the API.
It lets the store dispatches an action, but before dispatching this action some other data needs to be obtained first. Thus, dispatched action is dependent on data that needs to be fetched before the actual action is called and the fetched data is passed to the action.
this.d1 = this.store.pipe(select(selectData1));
this.d2 = this.store.pipe(select(selectData2));
// ...
// dispatch the actions below to and pass identifier (id) in order to get its object from the API and put the fetched data in store.
this.store.dispatch(action1({data1Id}));
this.store.dispatch(action1({data2Id}));
// dispatch the actual action and pass this.d1 and this.d2
this.store.dispatch(targetAction({this.d1, this.d2}));
What is the best practise for the situation described above?
One way you could do this is to subscribe to this.d1 and this.d2 and then dispatch the target action when they emit:
combineLatest([this.d1 this.d2])
.subscribe(([d1, d2]) => {
if (d1 && d2) {
this.store.dispatch(targetAction(d1, d2));
}
})
However, it might be better to use an effect for this:
https://ngrx.io/guide/effects

Meteor with Angular2 , Fetching all entries from a collection in single shot

I have successfully integeraed meteor with angular2 but while fetching the data from collection facing difficulties in getting at one shot, here is the steps:
Collection Name : OrderDetails
No Of records : 1000
Server:
Created publication file to subcribe the collection:
Meteor.publish('orderFilter', function() {
return OrderLineDetails.find({});
});
Client:
this.dateSubscription =
MeteorObservable.subscribe('orderFilter').subscribe(()=> {
let lines = OrderDetails.find({expectedShipDate:{$in:strArr}},{fields:
{"expectedShipDate":1,"loadNo":1},sort:{"expectedShipDate":1}}).fetch();
});
In this lines attribute fetches all the collection entries, but fails to subscribe for the changes
When I try with below one,
OrderDetails.find({expectedShipDate:{$in:strArr}},{fields:{"expectedShipDate":1,"loadNo":1},sort:{"expectedShipDate":1}}).zone().subscribe(results => {
// code to loop the results
});
In this am able to subscribe for the collection changes, but the results are looped for 1000 times , as 1000 entries in the colleciton.
Is there any way to get the whole collection entries in one single shot and mean time to subscribe the changes in the collection ?.
Yes, there are a couple of ways you can do it, mostly depending on how you want to handle the data.
If having everything at once is important, then use a Method such as:
MeteorObservable.call('getAllElements', (err, result) => {
// result.length === all elements
})
While on server side doing
Meteor.methods({
getAllElements:function(){return myCollection.find().fetch()}
})
Now, if you want to listen to changes, ofcourse you'll have to do a subscription, and if you want to lower the amount of subscriptions, use rxjs' debounceTime() function, such as (from your code):
this.theData.debounceTime(400).subscribe(value => ...., err =>)
This will wait a certain amount of time before subscribing to that collection.
Now, based on your intent: listening to changes and getting everything at once, you can combine both approaches, not the most efficient but can be effective.
As #Rager explained, observables are close to streams, so when you populate data on miniMongo (front end collection you use when you find() data and is populated when you subscribe to publications) it will start incrementing until the collection is in sync.
Since miniMongo is populated when you subscribe to a publication, and not when you query a cursor, you could either:
Try the debouceTime() approach
Use a Meteor.Method after subscribing to the publication, then sync both results, keeping the first response from the method as your starting point, and then using data from Collection.find().subscribe(collectionArray => ..., err=>) to do whatterver you want to do when changes apply (not that recommended, unless you have a specific use case for this)
Also, .zone() function is specific to force re-render on Angular's event cycle. I'd recomend not use it if you're processing the collections' data instead of rendering it on a ngFor* loop. And if you're using an ngFor* loop, use the async pipe instead ngFor="let entry of Collection | async"
I don't think that's possible. When you subscribe to an Observable it handles values as a "stream", not necessarily a loop. I have seen some makeshift helper methods that handle the data synchronously, though the time it takes to subscribe is not decreased. Check out this article for an under the hood look... A simple Observable implementation
However, you can set it up to only loop once.
The way that I've been setting up that scenario, the collection only gets looped through one time (in the constructor when the app starts) and detects changes in the collection. In your case it would look like:
values: YourModel[] = []; //this is an array of models to store the data
theData: Observable<YourModel[]>;
errors: string[];
subFinished: boolean = false;
constructor(){
this.theData = OrderDetails.find({expectedShipDate:{$in:strArr}},{fields:{"expectedShipDate":1,"loadNo":1},sort:{"expectedShipDate":1}}).zone();
MeteorObservable.subscribe('orderFilter').subscribe();
//push data onto the values array
this.theData.subscribe(
value => this.values = value,
error => this.errors.push("new error"),
() => this.subFinished = true
);
}
The "values" array is updated with whatever changes happen to the database.

Working with an LRU and redux store strategy

I wanted to implement an LRU for a react-redux application, however I'm not sure what the best strategy of reading and writing data to the store via reducer so that I can maintain the LRU structure.
The goal is to implement an LRU for a most recent list of users. Effectively, whenever the application click on a specific contact, they get added to the most recent list of users. Let's say the list max out at 10 users, so effectively when it hit the max i'll pop off the oldest access user on the list.
I could associate a timestamp for each user in the list, but that means every time I read the state from the store, I would have to sort and find the oldest time stamp which i feel is slow.
I'm new to React/Redux, so please bear with me.
Any suggestions appreciated!
Thanks,
Derek
I would just have a seperate reducer that acts on the "select contact" action (there is probably another reducer that will also act on to set the currently selected user). It will maintain the array and just push to the front, and if the max is reachers, pop off the end.
Something like:
const initialState = []
export const lruReducer = (state = initialState, action) => {
switch(action.type) {
case 'SELECT_CONTACT':
// copy the previous array (I'm assuming ES6 syntax here, but you could use Object.assign or ImmutableJS or something if preferred)
// this is important to keep the state immutable
let newState = [...state]
// add the new contact (this is where you would do any de-duping logic
newState.unshift(action.user)
// keep removing items until constraint is met
while (newState.length > 10) {
newState.pop()
}
// return new array
return newState
default:
return state
}
}
Then just combine this with your other reducers like normal.

Popup and Redux Reducers

I'd like to know how to handle specific use case with redux reducer. To give an example, say I have a form with a DataGrid/Table. On Edit button click from a row, I want to open the popup with seleted row-data. After editing the data, On Popup-Submit button click, I want to update the Table/DataGrid (i.e. DataGrid will now should have the edited values).
I've written two separate Components
1. MainPage.js and its corresponding reducer MainPageReducer (Employee List)
2. PopupPage.js and its corresponding reducer PopupPageReducer (Selected Employee)
How these two reducers share the state?
You may need to read this first
http://redux.js.org/docs/basics/UsageWithReact.html
The main concept is that through the connect function, you would simply map needed properties of your state to the properties of your component i.e MapStateToProps. So in your case, imagine that your state, for contrived purposes, is structed like so:
{employees: {employees: {1: {id: 1, name: 'Foo'}}, editedEmployeeId: 1}
You could map the array of employees to an employees property for your EmployeeList component whilst also mapping a dispatch function, named editEmployee(id) to a click function on each row in the table.
You could map [the employee with the associated editedEmployeeId] to the individual employee in your employees array for your popup component
It may be efficient to just use one reducer instead of two.
Specifically, if you're making an update to an individual employee then you would call an EDIT_EMPLOYEE action and then a SAVE_EMPLOYEE action on save. After the SAVE_EMPLOYEE action, then, I assume, you'd call a post method, and then react-redux would re-render your entire list.
It could look like this:
function employees(state = {editedEmployeeId: undefined, employees = []}, action) {
switch(action.type) {
case EDIT_EMPLOYEE:
return Object.assign({}, state, {editedEmployee: action.employee_id})
case SAVE_EMPLOYEE:
return Object.assign({}, state, {employees: action.employees});
default:
return state;
}
}
There are great holes in my answer because the question you're asking might be too broad; I'm presuming you don't fully understand how the connect, subscribe, and dispatch functions work.
Like one of the comments said, reducers don't share state. They simply take the previous version of your state and return another version of it.
Anyways, hope this helps. Read the redux docs!

Redux – Reducer depending on other state

I have a Redux app that is displaying a list of Properties based on a set of Filters (user input).
Quick description of my state:
filters – Filters values object...
properties – Repo of all properties available on page
visibleProperties – List of properties with current filters applied
The problem is when I dispatch & set a new filter value, I need to filter properties based on filters new state and to store the result in visibleProperties.
So I came up with this solution:
export function setBedroomFilter (value) {
return (dispatch, getState) => {
// 'SET_FILTER' action
dispatch(setFilter('bedroom', parseInt(value)))
// New state
const { filters, properties } = getState()
// 'FILTER_PROPERTIES' action (Depending on new state)
dispatch(filterProperties(properties, filters))
}
}
And visibleProperties reducer can do its work:
// case 'FILTER_PROPERTIES'...
visibleProperties = action.properties.filter(item => item.bedroom >= action.filters.bedroom)
Is this approach totally fine?
From the documentation of dispatch:
Dispatches an action. This is the only way to trigger a state change.
The store's reducing function will be called with the current
getState() result and the given action synchronously. Its return value
will be considered the next state. It will be returned from getState()
from now on, and the change listeners will immediately be notified.
It's a synchronous function and it's totally fine to use in the way you've described(as long as setFilter is synchronous). However, if you're doing asynchronous operation in setFilter,(assuming that Promise returned from setFilter) you should chain your dispatch calls like this:
dispatch(setFilter('bedroom', parseInt(value))).then(() => {
// New state
const { filters, properties } = getState()
// 'FILTER_PROPERTIES' action (Depending on new state)
dispatch(filterProperties(properties, filters))
}
Another option might be using selectors. Please check it out:
https://github.com/reactjs/reselect

Resources