In my react/redux app, I am setting states in my reducers using something like this :
state.set('search', myResults);
the state is an immutablejs Map(). The format of the object when toJS() is run on it (no longer an immutable object) is like so :
{
results: { ..all results in here }
}
So you'd have state.search.results.
and this works great, however I have a scenario where I need to "push" into the search part of this map. I was thinking merge would take care of this however it does not seem to work as I assumed it would. I have tried :
state.mergeIn('search', singleResult);
as well as
state.merge({ 'search': singleResult });
neither seem to be working. The intended result is to have that single result pushed into the state Map() (and not overriding it like with .set). So the desired result would end up looking like this :
{
results: { singleResult pushed or merged into here with other results }
}
I am unsure how to do this with immutablejs, any advice would be greatly appreciated. Thanks!
You need to use update for this. Merging does not concat arrays together in the key.
state.update('search', search => search.concat(singleResult));
Also be aware that if search contains a normal JS array - then the array itself is still an mutable data structure. Immutable.JS doesn't do deep conversion of datatypes unless you use fromJS().
Related
in the process of developing an application, I'm facing a question about whether I'm using Redux correctly.
I have a fav:[] in which I add product objects and render their list. However, in order for the data not to be lost, I have to copy this fav:[] to favCopy:[] and only after that execute .filter
Example code:
case "fav":
state.fav = action.payload.filter === 'all'
? state.favCopy
: state.favCopy.filter((item: any) => item[type] === action.payload.filter)
break;
I would like to understand how right I am by keeping the original array intact? Maybe there is a way not to multiply arrays and use only one state?
We would recommend not doing filtering directly in the reducer most of the time. Instead, keep the original array in state as-is, and then also store a description of how you want the filtering to be done. From there, use selector functions to derive the filtered value as needed:
https://redux.js.org/usage/deriving-data-selectors
I'm currently trying to fix a bug in a piece of software. I have a for loop where I'm iterating through some data and then, for each piece of data I'm creating a new property in an object with a specific value. Kind of something like this:
let myObject = {};
for(iterate through data) {
myObject[data.name] = data.content;
}
The problem is that, sometimes, data.name can have the same value, meaning that at a certain point some data might be overridden with a consequent loss of information.
I have tried to solve this problem by using an array of objects, but I simply cannot pass such a structure to the $project part of an aggregation.
I have a necessity to be able to pass the above-mentioned object the $project stage of a mongo aggregate. How can I solve this problem? Is there any way I can solve it by either changing my data structure or by using some tools in the project?
I'm trying out immutable.js in my new React project, but I'm having a blackout at adding a Map to an existing List of Maps in my state.
I would previously have written (using Arrays and Objects):
this.setState({
objectsArray: [...this.state.objectsArray, newObject];
})
But I'm struggling to find the proper equivalent with immutable.js. I've tried things along the lines of this, but with no success:
state.get('mapsList').merge(newMap);
I'm finding plenty of examples on the web merging Lists and merging Maps, but I'm having a List of Maps, and the official docs on immutable Lists are not exactly elaborate.
Links to resources and best practices regarding React/Redux state operations appreciated as well, thank you!
Your "old" code creates a new array with the same objects that already existed, plus a new object.
As far as I can see, Immutable's List#push does the same thing:
push()
Returns a new List with the provided values appended, starting at this List's size.
push(...values: Array<T>): List<T>
So:
this.setState(({mapsList}) => {
return {mapsList: mapsList.push(newMap)};
});
Side note: I'm using the callback version of setState because you're not allowed to do what your old code did. If you're setting new state based on existing state, you must use the callback; details.
you need to update the list with
state = state.updateIn(['mapsList'], function(list) { return list.push(newMap); })
that will update the mapsList List with the new newMap
I have a problem that I believe is due to a combination of my initial state in ngrx-store and trying to get just the initial response from an observable. I'm using datatables, and large amounts of data for a static report - so I just want the first "real" response from:
Observable.zip(this.sites$, this.devices$, this.machines$, this.machineContent$)
In my ngrx reducer I have the initial state defined as [] for each of these. However, at times there is (what I think is a race condition) where one of those observables will be returned as [], but the Observable.zip resolves, and it incorrectly renders my table at that point.
I've tried various combinations of:
Observable.zip(this.sites$.skip(1), this.devices$.skip(1), this.machines$.skip(1), this.machineContent$.skip(1))
and
Observable.zip(this.sites$.skip(1), this.devices$.skip(1), this.machines$.skip(1), this.machineContent$.skip(1))
.take(1)
And these work about 80% of the time.
I also have an observable of 'isLoaded' for each of those observables (machinesIsLoaded, machineContentIsLoaded, etc.) which I thought about using with .takeUntil, except that I'd have to check for each of these to return true, and it feels like I must be doing something wrong.
Any ideas?
First: make sure your reducer is not ever mutating the array in state, because that will cause you problems. Make sure you are cloning the array or using an ImmutableArray.
Second: instead of filter, use skipWhile
function isEmpty(table: any[]) : boolean { return !table || !table.length; }
Observable.zip(this.sites$.skipWhile(isEmpty), this.devices$.skipWhile(isEmpty), this.machines$.skipWhile(isEmpty), this.machineContent$.skipWhile(isEmpty));
This will ignore results until the arrays get populated and then always use results even if the arrays empty again.
To me Immutable.js reduces a lot of headaches and it's a great library, but now im facing with a trouble, my original object comes from the server but when I use any of it's functions like fromJS({myObj}) it works but saves a copy but sorted "a-z" and I'm making something that need the original structure to keep the components in the order that comes from the server, someone any Idea?
fromJS translates your objects into lists and maps by default. The former is ordered but not keyed, while the latter is keyed but not ordered, so neither fits your use case.
What you're looking for is an OrderedMap, which is a Map with an additional insertion order guarantee:
import { OrderedMap } from 'immutable';
const orderedMap = OrderedMap({key: "value"});
You can achieve it by still using fromJS: It has a second parameter called reviver, which can be used also for using OrderedMaps instead standard Maps:
import Immutable from 'immutable';
const reviver = (key, value) =>
Immutable.Iterable.isKeyed(value) ? value.toOrderedMap() : value.toList();
const data = Immutable.fromJS(js, reviver);
Javascript core objects explicitly provide no guarantees about key order. Immutable.Map (the expected result of your fromJS() call) just follows that.
If you want order, you should either specify the order as another property on each item, or, more conventionally, create an Immutable.List from an Array.
In other words, this sounds like a square peg/round hole problem. Make sure you're using the right data structure for your task.