M. Kahlen
M. Kahlen

Reputation: 361

Manipulating data in nested arrays in Redux with immutable.js

So, I've been working on making an APP in React Native for which i have programmed a RESTFul API in Java, which returns some data in JSON format. I will have a datastructure that looks something like this - it is also the initial state for this Reducer, I have simply deleted some of the values as they are irrelevant:

categories: [{
    id: 1,
    name: '',
    image: '',
    subcategories: [
        {
            name: '',
            id: 1,
            products: [{
                name: '',
                description: 'This is a good product',
                id: 55,
                quantity: 4
            }, {
                name: '',
                description: 'This is a good product',
                id: 3,
                quantity: 0
            }]
        },
        {
            name: '',
            id: 2,
            products: [{
                name: '',
                description: 'This is a good product',
                id: 4,
                quantity: 0

            }]
        }]
}, {
    id: 2,
    name: '',
    image: '',
    subcategories: [
        {
            name: '',
            id: 3,
            products: [{
                name: '',
                description: 'This is a good product',
                id: 15,
                quantity: 0

            }]
        }

    ]
}]

I will be saving this in my Redux store but where i struggle is when I have to update the quantity of a certain product with only the products id. So far I've found a solution using immutable.js but it is quite ugly and I'm really unsure if this is the way to go.

I've searched for solutions but have not yet found one with a solution without normalizing the datastructure. For now I want to see if I can avoid normalizing the data, as I want to keep the same format for posting stuff back to the server. (and for learning purposes)

My solution in my Reducer with immutable.js looks like this:

case types.ADD_PRODUCT:

        const oldState = fromJS(state).toMap();

        var findCategoryIndex = oldState.get('categories').findIndex(function (category) {
            return category.get("subcategories").find(function (subcategories) {
                return subcategories.get("products").find(function (product) {
                    return product.get("id") === action.productId;
                })
            })
        })

        var findSubcategoryIndex = oldState.getIn(['categories', findCategoryIndex.toString()]).get('subcategories').findIndex(function (subcategory) {
            return subcategory.get("products").find(function (product) {
                return product.get("id") === action.productId;
            });
        })

        var findProductIndex = oldState.getIn(['categories', findCategoryIndex.toString(), 'subcategories', findSubcategoryIndex.toString()]).get('products').findIndex(function (product) {
                return product.get("id") === action.productId;
        })


        var newState = oldState.setIn(['categories', findCategoryIndex.toString(),
            'subcategories', findSubcategoryIndex.toString(), 'products', findProductIndex.toString(), 'quantity'],
        oldState.getIn(['categories', findCategoryIndex.toString(), 'subcategories', findSubcategoryIndex.toString(), 'products', findProductIndex.toString(), 'quantity'])+1);


        const newStateJS = newState.toJS();


        return {...state, categories: [...newStateJS.categories]}

I know all this may seem overcomplicated for the case, but I am simply trying to learn different approaches to this as I am very new to everything that has to do with JavaScript.

I am not looking for optimization on the data format itself, but I am looking for ways to manipulate data in nested arrays in Redux

I hope to get some good feedback on this and hopefully find out if I am on the right track :)

EDIT: It works with spread operators aswell without using Immutable.js, but I don't really understand what the difference is. Is there a performance difference and why choose one over the other?

case types.ADD_PRODUCT:
        return {
            ...state,
            categories:[...state.categories.map(category => ({...category,
                subcategories:[...category.subcategories.map(subcategory => ({...subcategory,
                    products:[...subcategory.products.map(product => product.id === action.productId ? {...product, quantity: product.quantity+1} : product)]}))]}))]
        }

Upvotes: 0

Views: 1260

Answers (1)

Vincent Taing
Vincent Taing

Reputation: 3334

When things the data to become a bit too deep, you can still use helper like Immutable.js Map. I am not sure this is the correct way to use Immutable.js since I am also experimenting it. It lets you return new state in a less verbose way like this :

import { fromJS } from 'immutable';

export default function reducer(state = initialState, action) {    
  const iState = fromJS(state);// Immutable State
  switch (action.type) {
    case type.ACTION:
      return iState.setIn(['depth1','depth2', 'depth3'], 'foobar').toJS() 
// return a copy of the state in JavaScript
// {depth1: {depth2: {depth3: 'foobar'} } }
  default:
    return state;
}

and from what I heard, using Immutable is more performant but it adds an another dependency to your project. Careful tough, if you are using combineReducers(reducers), it expects to be pass a plain object, so be sure to pass a plain object and not an Immutable object.

You said you are not specifically looking for optimization on the data format itself. Correct me if I am wrong, I think normalizr will help you to gain in flatness and be easier to update your store

Upvotes: 3

Related Questions