【问题标题】:How does spread operator work in an array vs. obj?扩展运算符如何在数组与 obj 中工作?
【发布时间】:2017-10-20 19:32:17
【问题描述】:

我正在从 tutorial 中学习 Redux,但我不明白下面的扩展运算符如何在对象和数组中工作。如果...state 返回相同的东西,它如何在这两种情况下工作?我认为它只会返回一个数组,因此它将在SHUTTER_VIDEO_SUCCESS 中工作,因为除了action.videos 之外,它只会将状态内的任何内容传播到新数组中,但是这将如何在@987654326 中工作@ 案子?没有可放置的键。展开运算符从默认的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;
  }
}

【问题讨论】:

标签: javascript redux ecmascript-next


【解决方案1】:

更新

Spread 语法允许您将数组传播到对象中(数组在技术上是对象,就像 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 隔离到视频数组中。您需要手动管理所有状态树,如上所示。但是你的 reducer 可能不应该根据一个动作来切换它发回的数据类型。那将是一个不可预知的混乱。

如果您想将每个键分解为一个单独的 reducer,您将拥有 3 个(图像、视频和 selectedVideo)并使用 combineReducers 创建您的状态对象。

import { combineReducers } from 'redux'
// import your separate reducer functions

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

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

【讨论】:

  • 您解决了扩展语法如何与数组中的数组和对象中的对象一起使用,但我的问题是它如何与对象中的数组一起使用
  • 已更新以更具体地解决您提供的代码/问题。
  • 谢谢@shadymoses!但是当您使用我的示例时,您将传入的 initialState 从initialState.videos 更改为initialState,这是否意味着我给出的示例是错误的并且不应该工作?我看到了你的工作原理,因为你用一个对象更新了一个对象,但这并没有解决我的例子的 SELECTED_VIDEO 如何将一个数组(initialState.videos)传播到一个 obj(新状态)
  • 是的,您示例中的化简器将产生非常有问题的状态,因为数据类型将从数组更改为具有基于传入操作的嵌套属性的对象。我更新了答案以解决将数组传播到对象中的问题。我也在我的回答中解决了一些问题,但我建议阅读更多关于如何设置减速器的 redux 文档。
【解决方案2】:

根据教程:

create-react-app 预装了 babel-plugin-transform-object-rest-spread,它允许您使用 spread (...) 运算符以简洁的方式将可枚举的属性从一个对象复制到另一个对象。对于上下文,{ ...state, videos: action.videos } 评估为 Object.assign({}, state, action.videos)。

所以,这不是 ES6 的特性。它使用插件让您使用该功能。

链接:https://babeljs.io/docs/plugins/transform-object-rest-spread/

【讨论】:

  • 您解决了扩展语法如何与对象中的对象一起使用,但我的问题是它如何与对象中的数组一起使用
  • @stackjlei 在 SELECTED_VIDEO 情况下,返回一个对象。
【解决方案3】:

数组也是键/值对,但键是索引。它使用ES6 destructuringspread syntax

Redux docs on the subject

您可能还想阅读 ES6 属性值简写(或其他名称):

ES6 Object Literal in Depth

当你发现自己分配了一个与属性名称匹配的属性值时,你可以省略该属性值,它在 ES6 中是隐含的。

【讨论】:

  • @FelixKling 你是对的,把它改成“扩展语法”,谢谢!实际上并没有考虑我为什么称它为运营商,只是因为很多其他人都这样做了。
  • 那么SELECTED_VIDEO 返回了什么?最初作为数组的状态如何传播到对象中?那么它会是{0: [...], selectedVideo: action.video}吗?
  • 在他的示例中不会使用变量名作为键和值,因为他将视频传播到对象文字中,而不是为其分配视频。这是 { videos } vs { ...videos },结果会大不相同。
猜你喜欢
  • 2020-03-01
  • 2018-07-29
  • 2016-08-08
  • 2021-05-13
  • 1970-01-01
  • 1970-01-01
  • 2018-12-21
  • 2018-11-01
  • 2018-02-16
相关资源
最近更新 更多