从对象内部的数组中删除一项(redux / typescript / react)

时间:2019-12-18 02:24:43

标签: reactjs typescript redux

我正在使用本地json文件来存储一些数据,如下所示:

[
    {
        "name": "Sports",
        "todos": [
            {
                "id": 1,
                "title": "Play Football"
            },
            {
                "id": 2,
                "title": "Play Basketball"
            }
        ]
    },
    {
        "name": "Work",
        "todos": [
            {
                "id": 3,
                "title": "Study TS"
            },
            {
                "id": 4,
                "title": "Work Hard"
            }
        ]
    }
    ]

我制作了一个简化器,该简化器采用了以上两个类别,并在组件页面上实际显示了所有这些数据。

我现在正在尝试创建另一个reducer,以从每个类别内的todos数组中删除某个项目,但是我失败了。这是到目前为止我创建的动作和减速器

// actions

export const getCategories = () => {
    return async (dispatch: Dispatch) => {
        const response = await axios.get<Category[]>(url);

        dispatch({
            type: 'GET_ALL_CATEGORIES',
            payload: response.data
        });
    };
};

export const deleteTodo = (id: number) => {
    return {
        type: 'DELETE_A_TODO',
        payload: { id }
    };
};

// reducers
const categoriesReducer = (state: Category[] = [], action: Action) => {
    switch (action.type) {
        case 'GET_ALL_CATEGORIES':
            return action.payload;
        case 'DELETE_A_TODO':
            state.forEach(category => {
                return category.todos.filter(
                    todo => todo.id !== action.payload.id
                );
            });
        default:
            return state;
    }
};

export const reducers = combineReducers<ReduxStoreState>({
    categories: categoriesReducer
});

然后在组件内部成功完成deleteTodo动作的连接,但没有任何内容被删除,状态没有差异。

我在做什么错了?

3 个答案:

答案 0 :(得分:2)

替换

state.forEach(category => {
    return category.todos.filter(
        todo => todo.id !== action.payload.id
    );
});

具有:


return state.map(category => {
  return {
    ...category,
    todos: category.todos.filter(todo => todo.id !== action.payload.id)
  }
})

答案 1 :(得分:1)

我建议在deleteTodo动作的有效载荷中返回类别名称或更好的ID类别索引。

在代码的当前状态下,这是解决方案之一

const categoriesReducer = (state: Category[] = [], action: Action) => {
  switch (action.type) {
    case 'GET_ALL_CATEGORIES':
      return action.payload
    case 'DELETE_A_TODO': {
      const { id } = action.payload
      const categoryIndex = state
        .findIndex(category => category.todos.some(todo => todo.id === id))

      return state.map((category, i) => i === categoryIndex
        ? {
            ...category,
            todos: category.todos.filter(todo => todo.id !== id),
          }
        : category
      )
    }
    default:
      return state
  }
}

现在,如果您将类别索引和ID一起返回,则无需遍历每个todos数组以找到ID

export const deleteTodo = (id: number, categoryIndex: number) => {
  return {
    type: 'DELETE_A_TODO',
    payload: { id, categoryIndex }
  }
}

const categoriesReducer = (state: Category[] = [], action: Action) => {
  switch (action.type) {
    case 'GET_ALL_CATEGORIES':
      return action.payload
    case 'DELETE_A_TODO': {
      const { id, categoryIndex } = action.payload

      return state.map((category, i) => i === categoryIndex
        ? {
            ...category,
            todos: category.todos.filter(todo => todo.id !== id),
          }
        : category
      )
    }
    default:
      return state
  }
}

答案 2 :(得分:1)

在您的DELETE情况下,调用forEach方法实际上不会返回新的数组,并且您也不会返回新的状态对象,因此会出现问题。

您需要映射状态并返回具有更新的todo数组的新类别对象。可以使用以下代码来实现:

case 'DELETE_A_TODO':
    return state.map(category => {
        return {
            ...category,
            todos: category.todos.filter(
                todo => todo.id !== action.payload.id
            )
        };
    });

希望有帮助。