我正在从这个tutorial学习Redux,我不明白下面的散布运算符如何在对象和数组中工作。如果...state返回相同的内容,在两种情况下如何工作?我以为它会返回一个数组,所以它将在SHUTTER_VIDEO_SUCCESS内部工作,因为它将除了action.videos之外,将状态内的任何内容传播到新数组中,但是在SELECTED_VIDEO情况下它将如何工作?没有密钥可以放入。散布运算符不是默认initialState的数组,不是键值对,对吗?

initialState.js

export default {
  images: [],
  videos: []
};

someComponent.js
import initialState from './initialState';
import * as types from 'constants/actionTypes';

export default function ( state = initialState.videos, action ) {
  switch (action.type) {
    case types.SELECTED_VIDEO:
      return { ...state, selectedVideo: action.video }
    case types.SHUTTER_VIDEO_SUCCESS:
      return [...state, action.videos];
    default:
      return state;
  }
}

最佳答案

更新

扩展语法允许您将数组扩展为对象(从技术上讲,数组是对象,js中的大多数内容都是对象)。当您将数组扩展到对象中时,它将为每个数组项向对象添加一个key: value对,其中键是索引,值是存储在数组中该索引处的值。例如:

const arr = [1,2,3,4,5]
const obj = { ...arr } // { 0: 1, 1: 2, 2: 3, 3: 4, 4: 5 }

const arr2 = [{ name: 'x' }, { name: 'y' }]
const obj2 = { ...arr2 } // { 0: { name: 'x' }, 1: { name: 'y' } }

您还可以将字符串散布到数组和对象中。对于数组,其行为类似于String.prototype.split:
const txt = 'abcdefg'
const arr = [...txt] // ['a','b','c','d','e','f', 'g']

对于对象,它将按字符分割字符串,并按索引分配键:
const obj = { ...txt } // { 0:'a',1:'b',2:'c',3:'d',4:'e',5:'f',6:'g' }

因此,当您将数组扩展到一个对象中时,您可能会获得可以正常工作的数据。但是,如果您给出的示例是您实际使用的示例,那么您将遇到问题。见下文。

=============

对于redux中的reducer,当对数组使用传播语法时,它将数组中的每个项目传播到新数组中。它基本上与使用concat相同:
const arr = [1,2,3]
const arr2 = [4,5,6]
const arr3 = [...arr, ...arr2] // [1,2,3,4,5,6]
// same as arr.concat(arr2)

对于一个对象,传播语法将key: value对从一个对象传播到另一个对象:
const obj = { a: 1, b: 2, c: 3 }
const newObj = { ...obj, x: 4, y: 5, z: 6 }
// { a: 1, b: 2, c: 3, x: 4, y: 5, z: 6 }

这是两种方法,可帮助您在 reducer 中保持数据不变。扩展语法复制数组项或对象键/值,而不是引用它们。如果对嵌套对象或数组中的对象进行了任何更改,则必须考虑到这一点,以确保获得新副本而不是突变数据。

如果将数组作为对象键,则可以将整个对象扩展到一个新对象,然后根据需要覆盖单个键,包括需要使用扩展语法更新的数组键。例如,对示例代码的更新:
const initialState = {
  images: [],
  videos: [],
  selectedVideo: ''
}

// you need all of your initialState here, not just one of the keys
export default function ( state = initialState, action ) {
  switch (action.type) {
    case types.SELECTED_VIDEO:
      // spread all the existing data into your new state, replacing only the selectedVideo key
      return {
        ...state,
        selectedVideo: action.video
      }
    case types.SHUTTER_VIDEO_SUCCESS:
      // spread current state into new state, replacing videos with the current state videos and the action videos
      return {
        ...state,
        videos: [...state.videos, ...action.videos]
      }
    default:
      return state;
  }
}

这显示了更新状态对象和该对象的特定键(它们是数组)。

在您提供的示例中,您正在动态更改状态的结构。它以数组开始,然后有时返回一个数组(在SHUTTER_VIDEO_SUCCESS时),有时返回一个对象(在SELECTED_VIDEO时)。如果要具有单个reducer函数,则不会将initialState仅隔离到video数组。您将需要手动管理所有状态树,如上所示。但是 reducer 可能不应该根据操作切换回传的数据类型。那将是无法预测的困惑。

如果要将每个键分解到单独的化简器中,则需要3个(图像,视频和selectedVideo),并使用combineReducers创建状态对象。
import { combineReducers } from 'redux'
// import your separate reducer functions

export default combineReucers({
  images,
  videos,
  selectedVideos
})

在这种情况下,每当您调度一个操作以生成完整的状态对象时,每个reducer都将运行。但是每个化简器只会处理其特定的键,而不是整个状态对象。因此,您只需要对数组等键进行数组更新逻辑。

10-04 22:29
查看更多