最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How do I swap array elements in an immutable fashion within a Redux reducer? - Stack Overflow

programmeradmin8浏览0评论

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)
 })

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)
 })
Share Improve this question asked Dec 13, 2016 at 17:52 therewillbecodetherewillbecode 7,1804 gold badges37 silver badges42 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 12

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.

You could use map function to make a swap:

function immutablySwapItems(items, firstIndex, secondIndex) {
    return items.map(function(element, index) {
        if (index === firstIndex) return items[secondIndex];
        else if (index === secondIndex) return items[firstIndex];
        else return element;
    }
}

In ES2015 style:

const immutablySwapItems = (items, firstIndex, secondIndex) =>
  items.map(
    (element, index) =>
      index === firstIndex
        ? items[secondIndex]
        : index === secondIndex
        ? items[firstIndex]
        : element
  )

There is nothing wrong with the other two answers, but I think there is even a simpler way to do it with ES6.

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

const immutableSwap = (items, firstIndex, secondIndex) => {
  const result = [...items];
  [result[firstIndex], result[secondIndex]] = [result[secondIndex], result[firstIndex]];
  return result;

}

const swapped = immutableSwap(state, 2, 0);

console.log("Swapped:", swapped);
console.log("Original:", state);

发布评论

评论列表(0)

  1. 暂无评论