Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I swap array elements in an immutable fashion within a Redux reducer?

The relevant Redux state consists of an array of objects representing layers.

Example:

let state = [
    { id: 1 }, { id: 2 }, { id: 3 }
]

I have a Redux action called moveLayerIndex:

actions.js

export const moveLayerIndex = (id, destinationIndex) => ({
    type: MOVE_LAYER_INDEX,
    id,
    destinationIndex
})

I would like the reducer to handle the action by swapping the position of the elements in the array.

reducers/layers.js

const layers = (state=[], action) => {
    switch(action.type) {
        case 'MOVE_LAYER_INDEX':

/* What should I put here to make the below test pass */

        default:
         return state
    }
}

The test verifies that a the Redux reducer swaps an array's elements in immutable fashion.

Deep-freeze is used to check the initial state is not mutated in any way.

How do I make this test pass?

test/reducers/index.js

import { expect } from 'chai'
import deepFreeze from'deep-freeze'

const id=1
const destinationIndex=1

 it('move position of layer', () => {
    const action = actions.moveLayerIndex(id, destinationIndex)
    const initialState = [
        {
          id: 1
        },
        {
          id: 2
        },
        {
          id: 3
        }
    ]
    const expectedState = [
        {
          id: 2
        },
        {
          id: 1
        },
        {
          id: 3
        }
    ]
    deepFreeze(initialState)
    expect(layers(initialState, action)).to.eql(expectedState)
 })
like image 476
therewillbecode Avatar asked Dec 13 '16 17:12

therewillbecode


1 Answers

One of the key ideas of immutable updates is that while you should never directly modify the original items, it's okay to make a copy and mutate the copy before returning it.

With that in mind, this function should do what you want:

function immutablySwapItems(items, firstIndex, secondIndex) {
    // Constant reference - we can still modify the array itself
    const results= items.slice();
    const firstItem = items[firstIndex];
    results[firstIndex] = items[secondIndex];
    results[secondIndex] = firstItem;

    return results;
}

I wrote a section for the Redux docs called Structuring Reducers - Immutable Update Patterns which gives examples of some related ways to update data.

like image 101
markerikson Avatar answered Sep 30 '22 19:09

markerikson