So I am having trouble updating in the reducer with Immutable.js.
So lets say I have a an initial state in my reducer as so:
Immutable.Map(action.someData)
where someData is a JSON, I received from a server:
someData: {
contactInfo: {phoneNumber: 123-1253}
address: 101 e street
family: {
husband: "Bob"
wife: "Sally"
}
}
If I wanted to update the family.husband, how would I go on to do it?
since someData is just a regular object, does this not allow me to use setIn method?
Looking into Immutable documentation, I think the correct way is to create immutable structures on all levels, that is:
Immutable.fromJS(action.someData)
then you will be able to use setIn.
If you keep the internal objects as plain javascript objects, they are still mutable.
Related
I've recently learned React and Redux, and right when I thought I was getting the hang of it I've been hung up on this.
In my reducer I'm under the impression that the spread operator should create a new instance of the object at a difference memory location. However, using === to compare the objects returns true which I believe means the objects are the same instances.
const initState = {
loadcases: {
1: {
isActive: false,
profile: 'UB'
},
2: {
isActive: true,
profile: 'PFC'
}
}
}
const designInput = (state=initState, action={}) => {
let newState = {...state}; //clone design input state
console.log(newState.loadcases === state.loadcases)
// Prints: TRUE ---- WHYYYYYYY????????????
// goes on to modify newState before returning it.
}
The redux logger is showing my previous state to already equal the new state which has me thinking that this here is interfering with the store directly. I've read through some redux docs and other posts which make me believe I have set everything else up correctly.
Any resources to educate me more on this issue is also much appreciated.
UPDATE:
Jayce's answer was exactly what was happening.
This SO post outlines which method for a deep clone will be suitable. For me, it's likely I'll want to store dates and other data so I've chosen to use lodash cloneDeep method.
So now: console.log(cloneDeep(state) === state.loadcases); // false
Your assumption that the spread operator creates a new object is true, however it doesn't do a deep clone of objects. It copies the references of nested values over to the new object, so that's why it's printing that they're equal.
If you want to deeply clone something with nested values you can't use spread, you have to use another method. You could use a library like lodash, or something such as JSON.parse(JSON.stringify(obj)), there's multiple options just google for something like "Javascript deep clone object"
I'm coding an application using React for UI, Redux to manage the state and Immutable.js to mutate the state, however, I'd like to know how to avoid the use of Immutable.JS accessors in my React components, like get() or getIn().
I believe that using that Immutable.JS accessors will infect my React components. How to avoid that?
I don't think you're going to have much in the way of an option here if you want to keep it immutable. You could convert it toJS, but then you'd be losing the benefits of object identity comparison for re-rendering pure components. Your best bet is probably to hold your nose and pretend it's basically a JavaScript Map.
Aside from that, if you're not attached to Immutable.js, you might consider using something like seamless immutable which behaves a lot more like native JavaScript arrays and objects. Or you could go old-fashioned and just Object.freeze() things yourself.
The way to avoid Immutable.JS accessors and use the dot-notation is using the Record structure from Immutable.JS.
First we must create a template:
const MyTemplate = Record({
id: 0,
name: ''
})
...
case ContentFilterTypes.ADD_ITEM:
return {
listObjects : state.listObjects.push(new MyTemplate({
id: action.item.id,
name: action.item.description,
}))
}
To access it in the presentional, we need only to set the prop that we want to get information, like:
<ContentFilterAvatar id={value.id} name={value.name} />
This is something I'am not getting right.
While using redux, in reducers we use the spread operator.
For e.g.
{...state,data : action.payload,fetching:false}
That is a new state object is created, rather than mutating the correct state right? (Please correct me if i'am wrong)
In such cases what is the use of immutableJS ??
It performs the same action as mentioned above right??
You are correct, the example you have shown is creating a new object and not mutating the state. It is fine for many cases, so if you don't feel that ImmutableJS is going to add anything for you, don't use it.
ImmutableJS was more useful before the spread operator was in common use in ES6 (I believe it is technically still only a proposal). If you are not using ES6, then the alternative is to use Object.assign which can get very messy, very quickly, especially with more nested structures.
ImmutableJS is still useful if you need to modify a single node deep within the state tree, but if this is the case, you can generally get around it by structuring the data in a different way.
When you have a simple flat state, you can easily manage it without extra libraries.
But let's consider something more complex, like the following
{
users: {
123: {
name: 'John',
lastName: 'Doe'
},
345: {
name: 'Bob',
lastName: 'Jack'
}
....
}
}
If you want to update a name for some user, it will be not so trivial
return {
...state,
users: {
...state.users,
[action.userId]: {
...state.users[action.userId],
name: action.newName
}
}
Pretty much code, isn't it? At this moment, you may want to look for another solution and immutable.js may help you do the same with one line:
state.setIn(['users', action.userId, 'name'], action.newName)
Making your state immutable ensures you that the state will not get modified outside of flux-flow. In very complex structures with a lot of levels and props being passed around it, it prevents the state from being mutated accidentally.
What if I have very common situation like.
users:[
{
firstName:'Jack',
lastName:'Daniels'
}
]
I want to have getFullName, method, what is a proper way to do that in Redux, we all know that it's possible to make it markup. I have very complicated data structures and it's not that easy to make it in markup
In JavaScript, there is no need to couple the data and the methods that act on that data. Redux stores an immutable state object without methods. You just need to find somewhere else to put those methods. In this case, I would create a utils/people.js that exposes all the methods that are required to work on people. You can then pass the state from redux to that method and get everything you need.
Assuming your state in Redux looks something like:
{ firstName: 'John', lastName: 'Smith' }
You can have utils/people.js:
exports getFullName(state) {
return `#{state.firstName} #{state.lastName}`;
}
So I have a React component where I have:
getInitialState: function () {
return {
peopleDetails: []
}
}
peopleDetails will contain objects which will have property names like name, email, phone etc.
Here's the problem: I'll be passing methods to child components which should mutate the objects of this array. Some methods will just edit a person's email, another will edit the phone etc. Thus, they'll need to call setState.
As far as I understand, setState should return a new copy of the object. I find this impossible to do because things are difficult enough even if I have to mutate the object (I'll have to specify some filters to find it inside an array, then change its value etc).
I'm pretty sure a lot of React apps use data structures like these...where in the state you have an array holding objects and you'll need to do stuff to those objects. Is there some easy way to accomplish this, respecting the "rule" that each setState invocation should return a completely new state object?
If you really don't want to use immutable data, then you can directly mutate the properties you like and invoke a this.forceUpdate() instead of setState after mutations have occurred.
If you were to proceed with immutable data, then adding to that array would return a new array containing the new object. If you removed that person, you'd then return a new empty array. If you were to modify an item in-place in the array, then you'd build a new array using concat/slice to get all elements that haven't changed, then add your changed element, then add everything after the changed element's index.
For a more in-depth discussion on avoiding array mutations, check out this talk.