【问题标题】:Nested array maps inside of reducerreducer 内的嵌套数组映射
【发布时间】:2021-12-29 15:16:55
【问题描述】:

我正在处理一个复杂的 redux 情况,但无法完全得到我需要的东西。我有 2 个对象,TrackTarget

interface Track {
  id: number,
  ...other fields
}

interface Target {
  id: number (same as the Track)
  tracks: Track[]
  ...other fields
}

我想要做的是,当我获取轨道时,我试图查看是否有任何目标具有相同的 ID(一个目标对多个轨道),如果有,则将轨道添加到该目标的轨道数组,否则 - 创建一个具有该 Track id 的新目标并将该轨道放入轨道数组中

case TargetActionTypes.FETCH_TARGETS_SUCCESS:
  return {
    ...state,
    targets: [
      action.tracks.map((track: Track) =>
        state.targets.map((target: Target) =>
          track.target_id === target.id
            ? {
                ...target,
                tracks: [...target.tracks, track]
              }
            : {
                id: track.target_id,
                visible: true,
                tracks: [track]
              }
        )
      )
    ]
  }

我认为这是在正确的轨道上,但打字稿在抱怨,因为我认为它的嵌套内容可能由于嵌套循环而太深了一层?这里有打字稿错误

Types of property 'targets' are incompatible. Type '{ tracks: Track[]; id: number; visible: boolean; }[][][]' is not assignable to type 'Target[]'.
Type '{ tracks: Track[]; id: number; visible: boolean; }[][]' is missing the following properties from type 'Target': id, visible, tracks",

【问题讨论】:

  • 所以父级设置为Target[]类型,但是映射返回一个嵌套数组,这就是TS抱怨的原因,你能不能用硬编码的响应在普通的香草JS中试试这个,这样我们就知道我们至少得到了什么 o/p,并且我们可以弄清楚

标签: javascript reactjs typescript redux react-redux


【解决方案1】:

如 cmets 中所述,您所在州的属性 targets 具有类型 Target[],但您分配给它的不是。 state.targets.map(...)返回一个目标数组,所以Target[],然后它从回调函数返回到另一个map,所以action.tracks.map(...)返回一个Target的数组数组(所以Target[][])然后你包装它放在一对方括号中,所以它变成Target[][][]。调用.map 返回一个数组,所以如果你用方括号括起来,你会得到数组中的数组,所以第一步是删除那些。

现在要摆脱另一层嵌套,您将需要摆脱.map,这在这种情况下不太适用,因为.map 只是将一个数组转换为另一个具有相同长度的数组。这里的长度可以变化,所以.map 不是很合适。最简单的方法是不使用内置方法并使用循环:

const targets: Target[] = []
for(const track of tracks) {
  const target = targets.find(target => target.id === track.target_id)

  if (target) target.push(track)
  else targets.push({id: track.target_id, visible: true, tracks: [track]})
}

也许您可以通过在每次迭代中不遍历整个 targets 数组来使该算法更快一些

const targets: Target[] = []
const cache: Record<number, Target> = {}

for(const track of tracks) {
  if(track.target_id in cache) {
    cache[track.target_id].tracks.push(track)
  } else {
    targets.push(cache[track.target_id] = {
      id: track.target_id, 
      visible: true, 
      tracks: [track]
    })
  }
}

如果你真的想要一些内置方法,那么.reduce 会更合适:

tracks.reduce<Target[]>(
  (targets, track) => {
    const target = targets.find(target => target.id === track.track_id)

    if(!target) {
      targets.push({id: track.target_id, visible: true, tracks: [track]})
    } else {
      target.tracks.push(track)
    }
    return targets
    
  }
)

或者如果你不喜欢改变对象

tracks.reduce<Target[]>(
  (targets, track) => {
    const index = targets.findIndex(target => target.id === track.target_id)

    if(index < 0) {
      return [...targets, {id: track.target_id, visible: true, tracks: [track]}]
    } 
    return [
      ...targets.slice(0, index),
      {...targets[index], tracks: [...targets[index].tracks, track]},
      ...targets.slice(index),
    ]
  }
)

但我看不出这段代码有多大优势,因为它的可读性不是很好,而且在数组之间不必要地复制元素很多次

【讨论】:

    猜你喜欢
    • 2021-05-27
    • 2020-09-25
    • 2019-08-13
    • 1970-01-01
    • 2013-03-21
    • 1970-01-01
    • 2013-08-08
    • 2013-07-17
    • 2021-02-08
    相关资源
    最近更新 更多