How to handle nested array structure in Redux combineReducers - redux

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

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.

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

How to mount the redux-form further down in a nested state structure?

I am stuck here on how to mount the redux-form reducer at the right place.
I have this initial state for a namespace called person (among many other namespaces in the app), something like this:
{
personList: [],
creatingNewPerson: false,
newPerson: {}
}
the newPerson state is a form. How can I tell to have a redux-reducer acting on the newPerson state alone?
sure, you could do something like
combineReducers({
person: personReducer, // that's a reducer using the above json
newPerson: formReducer // import { reducer as formReducer } from 'redux-form'
})
but that's not the structure I am after. The state for the newPerson will be managed outside of the person state. But I want it to be managed inside.
It should be possible when the states redux-form is managing are JSON serializable.
How can this be achieved? Hope I made myself clear enough?
Unfortunately redux-form seems quite opinionated in this particular case, and the general sentiment from the documentation is quite clear: You should mount the formReducer at the root of your state tree under the form -key.
The reason for this is simple: the formReducer will handle the state for all of your forms, not just the new person form. So the state will look something like this:
{
person: { ... person-related state ... },
form: {
NewPersonForm: { ... new person form state ... },
SomeOtherForm: { ... some other form state ... },
...
NthAdditionalForm: { ... nth additional form state ... }
}
}
This means that if you'd want to position the state for each form nested inside the reducer the resulting object will end up in, you'd have to add in instances of formReducer in multiple locations, which would unnecessarily complicate your state.
My recommendation: Eat your proverbial greens in this case and just insert the formReducer in the default location, because that way you'll get the enjoy the power of redux-form without any additional headaches.
Now, after reading the above, if you're still dead set on actually mounting the formReducer somewhere deep within the damp and dark mazes of your state tree, you could do something like the following:
combineReducers({
persons: combineReducers({
person: personReducer,
newPerson: formReducer
}),
other: otherReducer,
...
some: someReducer
})
Then you also need to pass a getFormState to each reduxForm -wrapped component so they know where you hid their state:
const getFormState = state => {
// return the slice of state where we hid formReducer
return state.persons.newPerson
}
reduxForm({ getFormState })(SomeForm)
Doing this is something I cannot, with good conscience, recommend, but should produce the results you want (in addition to possible nasty side-effects if you ever add more than this one form to your app).
Hope this helps!

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.

Deep conversion of mutable data to immutable data with ImmutableJS

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.

Resources