Deep conversion of mutable data to immutable data with ImmutableJS - redux

I keep my head cracking over the following problem. I load some data from a REST API which I then want to convert into a immutable state for redux.
My data looks something like this:
{
name: 'Some string',
components: [
{
type: 'SomeType',
name: 'Another string,
components: [
{
// and so on...
}
]
}
]
}
As you can tell, the data gets nested quite deeply and Immutables Map and List methods only convert the first level.
I was wondering how I could convert every Object and Array to an immutable state.

Ok, turns out it is pretty forward.
I just need to use Immutable.fromJS.

Related

Updating normalised data without causing more re-renders outside of the state slice that has been updated

I have some normalised data (items) within my redux store:
{
items: {
index: ['a','b'],
dict: {
a: {
title: "red",
},
b: {
title: "car",
}
}
},
...
}
So, if I want to update anything within an item object, the reducer looks like this:
...
const itemsReducer = (state = initialState.items, action) => {
switch (action.type) {
case itemsActions.types.UPDATE_ITEM: {
return {
...state,
[action.payload.itemId]: {
title: action.payload.title,
}
}
}
default: return state;
}
};
But this technique creates a new object for items, which can cause unnecessary components to re-render, when really it should only cause components that subscribe to state changes of the individual object to re-render.
Is there any way to get around this?
That is how immutable updates are required to work - you must create copies of every level of nesting that needs to be updated.
In general, components should extract the smallest amount of data that they need from the store, to help minimize the chance of unnecessary re-renders. For example, most of the time a component probably shouldn't be reading the entire state.items slice.
FWIW, it looks like you're hand-writing your reducer logic. You should be using our official Redux Toolkit package to write your Redux logic in general. RTK also specifically has a createEntityAdapter API that will do most typical normalized state updates for you, so you don't have to write reducer logic by hand.
I'll also note that the recently released Reselect 4.1 version has new options you can use for customizing memoized selectors as well.

nuxt component : computed vs data

In my nuxt component, I can't understand the difference between computed and data. I get the difference between data and asyncData but there is nothing regarding those two attributes.
<template>
{{computedMessage}}
{{dataMessage}}
</template>
<script>
export default {
computed: {
computedMessage(){
return this.$store.state.whatever;
}
},
data() {
return {
dataMessage: "Hi there"
}
}
}
</script>
If data is 100% static, then why make it a function?
If I want to have process.env in the function, should it be in computed or in data?
The difference between computed and data is not reactivity. Both are fully reactive, as seen here. The real difference between the two is essentially this:
data is made up of properties
computed is made up of getters.
They both serve very different purposes, but together give you some powerful tools for data manipulation.
For example:
export default {
mounted() {
console.log(this.adults)
}
data() {
return {
users: [
{ name: 'Jack', age: 12 },
{ name: 'Jill', age: 53 },
{ name: 'Smith', age: 29 },
{ name: 'Matt', age: 18 }
]
}
},
computed: {
adults() {
return this.users.filter(user => user.age >= 18)
}
}
}
This example will return 3 users from this.adults: Jill, Smith, and Matt. Without a computed property, you'd need to call a method to compute this, and you'd need to re-call it every single time you need to access that data again.
What's great about using computed properties is that their results are cached, similar to Vuex getters. This can obviously have some huge benefits when working with larger data sets.
So in summary, data is used for storing data, and computed is used for calculating new results based on data without needing to change the original state.
So now to your points:
If data is 100% static, then why make it a function?
This is because Vue shares references to all its properties, including data, between instances of the same component. So instead of declaring a plain data object, we declare a function that returns a fresh version each time it's instantiated.
If I want to have process.env in the function, should it be in computed or in data?
You're able to access process.env in either computed or data. In general, if you have access to the Nuxt instance using this, then chances are you also have access to the process.env properties.
Well the difference between data and computed is that computed is reactive and data is static. So if you want to use data that gets automatically updated, you have to use computed.
computedis for example often used when you have to wait for data (e.g. from REST api), but you don't want to block your UI. So you assign a computedvariable and the part of your UI is updated when the data has arrived.
To understand, why data needs to be a function, you should have a look at this.

How can I update deeply nested object inside array?

Hello good people of the stack!
I am working on a react-redux application and I am trying to update a property on a deeply nested structure in my reducer. The data structure is as follows and I want to update the text property:
state = {
assessment: {
requirements: [
questions: [
{
text
}
]
]
}
}
so I have tried the following:
// reducer code...
return {
...state,
[assessmentId]: {
...state[assessmentId],
requirements: [
...state[assessmentId].requirements,
[requirementId]: [
...state[assessmentId].requirements[requirementsId],
questions: [
...state[assessmentId].requirements[requirementsId].questions,
[questionId]: {
text: action.payload.response.text
},
],
] ,
],
},
};
This is more pseudo code than actual code to remove complexity.
I do not see any change in redux dev tools so I am wondering if I have made a mistake the way I get the nested objects and array elements.
I was also curious about using combine reducers here. I asked a colleague and they suggested to use that but I am unsure how you would take that approach here. As always, any help is appreciated.
I recommend immer for deep state changes in your reducers.
It adds a little weight to your bundle, and you'll get better performance from using the spread operator, but if you can live with that it'll make your code easier to read and write.
import produce from "immer";
// reducer code...
return produce(state, draft => {
draft[assessmentId].requirements[requirementsId].questions[questionsIndex].text = action.payload.response.text;
});
I'd say your issue stems from questions being an array which will take a little more work to keep straight than object based state.
As it is you appear to be trying to set the question value as if questions was an object. Maybe you just need to drop the [questionId] syntax, eg
questions: [
...state[assessmentId].requirements[requirementsId].questions,
{ text: action.payload.response.text },
],
This will set the text object as a new item on the end of the array though.
Depending on what you need to do (ie what already exists in the array and whether you are trying to add or update) you'll want to have a read of:
https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns#inserting-and-removing-items-in-arrays
https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns#updating-an-item-in-an-array

Normalizr and recursive nested structure

I'm using normalizr to flatten a structure like the following one:
{
"fields":[{
"id":"29",
"text": "something"
}, {
"id":"16",
"text": "something"
"fields":[{
"id":"17",
"text": "something"
}]
}, {
"id":"18",
"text": "something"
}
}
My structure has an array of fields and a field may also have nested fields. There's only one level of nesting allowed.
What I'm trying to do is this:
const block = new schema.Entity('fields')
const blockList = new schema.Array(block)
block.define({
fields: blockList
})
const normalizedData = normalize(originalData, blockList)
After running this snippet, normalizedData has a results property and it only has the first level of field ids, even though entities has all the fields normalized, including the nested ones.
I'd like to have in the results array all the ids, including the nested ones. What am I missing?
I'd like to have in the results array all the ids, including the nested ones. What am I missing?
This is how Normalizr works. The returned result is always in the same format of the input data. You won't be able to get what you are asking for from Normalizr.
If, however, you're just looking for a list of blocks, pull that from the entities:
const blockIds = Object.keys(normalizedData.entities.blocks);
You should consider using a normalized form for your data structure in redux.
This is advisable if your application need growth in complexity.
There is an interesting article on redux docs.
http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html
A normalized form take some ideas from db counterpart and Normalizr works in that way, so your request does not really match with how Normalizr works.
Please consider #Paul Armstrong answer for a work around if you really need get blocks in that way.

How to handle nested array structure in Redux combineReducers

ALl:
I'm pretty new to Redux reducer. I wonder if I have a nested array structure data, how can I build that combined reducer even I can normalize the data.
Any example for this?
For example, my data structure is like:
[
{
category:"c1",
subcategories: [{name: "sc1", items: [{name: "i1"}]}...]
}
...
]
How can I turn it into a structure that can apply combineReducer()? Thanks

Resources