React Redux将项目添加到列表中会转移到其他创建的列表中

React Redux adding items to list carries over to other created lists.

本文关键字:列表 转移 其他 创建 Redux 添加 React 项目      更新时间:2023-11-30

我已经和这个问题斗争了太久,有人能给我指明正确的方向吗?问题:当我创建一个列表时,我可以更新/删除它。我还可以向其中添加项目并更新/删除这些项目。当我添加另一个列表时,前者的项目会转移到后者,然后我无法编辑这些项目。如果我删除"列表"而不刷新浏览器,则项目仍在列表中。我需要一种方法将两者联系在一起,使列表只知道其项目提前感谢您的帮助。

/actions/lists.js

export const CREATE_LIST = 'CREATE_LIST'
export function createList(list) {
  return {
    type: CREATE_LIST,
    id: uuid.v4(),
    items: list.items || [],
    ...list
  }
}
export const CONNECT_TO_LIST = 'CONNECT_TO_LIST'
export function connectToList(listId, itemId) {
  return {
    type: CONNECT_TO_LIST,
    listId,
    itemId
  }
}
export const DISCONNECT_FROM_LIST = 'DISCONNECT_FROM_LIST'
export function disconnectFromList(listId, itemId) {
  return {
    type: DISCONNECT_FROM_LIST,
    listId,
    itemId
  }
}

/actions/items.js

export const CREATE_ITEM = 'CREATE_ITEM'
export function createItem(item) {
  return {
    type: CREATE_ITEM,
    item: {
      id: uuid.v4(),
      ...item
    }
  }
}
export const UPDATE_ITEM = 'UPDATE_ITEM'
export function updateItem(updatedItem) {
  return {
    type: UPDATE_ITEM,
    ...updatedItem
  }
}

/减速器/列表.js

import * as types from '../actions/lists'
const initialState = []
export default function lists(state = initialState, action) {
  switch (action.type) {
    case types.CREATE_LIST:
      return [
        ...state,
        {
          id: action.id,
          title: action.title,
          items: action.items || []
        }
      ]
    case types.UPDATE_LIST:
      return state.map((list) => {
        if(list.id === action.id) {
          return Object.assign({}, list, action)
        }
        return list
      })
    case types.CONNECT_TO_LIST:
      const listId = action.listId
      const itemId = action.itemId
      return state.map((list) => {
        const index = list.items.indexOf(itemId)
        if(index >= 0) {
          return Object.assign({}, list, {
            items: list.items.length > 1 ? list.items.slice(0, index).concat(
              list.items.slice(index + 1)): []
          })
        }
        if(list.id === listId) {
          return Object.assign({}, list, {
            items: [...list.items, itemId]
          })
        }
        return list
      })
    case types.DISCONNECT_FROM_LIST:
      return state.map((list) => {
        if(list.id === action.listId) {
          return Object.assign({}, list, {
            items: list.items.filter((id) => id !== action.itemId)
          })
        }
        return list
      })
    default:
      return state
  }
}

/异径管/items.js

import * as types from '../actions/items'
const initialState = []
export default function items(state = initialState, action) {
  switch (action.type) {
    case types.CREATE_ITEM:
      return [ ...state, action.item ]
    case types.UPDATE_ITEM:
      return state.map((item) => {
        if(item.id === action.id) {
          return Object.assign({}, item, action)
        }
        return item
      })
    case types.DELETE_ITEM:
      return state.filter((item) => item.id !== action.id )
    default:
      return state
  }
}

/components/List.jsx

import React from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import Items from './Items'
import Editor from './Editor'
import * as listActionCreators from '../actions/lists'
import * as itemActionCreators from '../actions/items'
export default class List extends React.Component {
  render() {
    const { list, updateList, ...props } = this.props
    const listId = list.id
    return (
      <div {...props}>
        <div className="list-header"
          onClick={() => props.listActions.updateList({id: listId, isEditing: true})}
        >
          <div className="list-add-item">
            <button onClick={this.addItem.bind(this, listId)}>+</button>
          </div>
          <Editor 
            className="list-title"
            isEditing={list.isEditing}
            value={list.title}
            onEdit={title => props.listActions.updateList({id: listId, title, isEditing: false})} 
          />
          <div className="list-delete">
            <button onClick={this.deleteList.bind(this, listId)}>x</button>
          </div>
        </div>
        <Items
          items={this.listItems}
          onValueClick={id => props.itemActions.updateItem({id, isEditing: true})}
          onEdit={(id, text) => props.itemActions.updateItem({id, text, isEditing: false})}
          onDelete={itemId => this.deleteItem(listId, itemId)}
        />
      </div>
    )
  }
  listItems() {
    props.list.items.map(id => state.items[
      state.items.findIndex(item => item.id === id)
    ]).filter(item => item)
  }
  deleteList(listId, e) {
    e.stopPropagation()
    this.props.listActions.deleteList(listId)
  } 
  addItem(listId, event) {
    event.stopPropagation()
    const item = this.props.itemActions.createItem({
      text: 'New Shopping Item'
    })
    this.props.listActions.connectToList(listId, item.id)
  }
  deleteItem(listId, itemId) {
    this.props.listActions.disconnectFromList(listId, itemId)
    this.props.itemActions.deleteItem(itemId)
  }
}
function mapStateToProps(state) {
  return {
    lists: state.lists,
    items: state.items
  }
}
function mapDispatchToProps(dispatch) {
  return {
    listActions: bindActionCreators(listActionCreators, dispatch),
    itemActions: bindActionCreators(itemActionCreators, dispatch)
  }
}
export default connect(mapStateToProps, mapDispatchToProps)(List)

/components/List.jsx

import React from 'react'
import List from './List.jsx'
export default ({lists}) => {
  return (
    <div className="lists">{lists.map((list) =>
      <List className="list" key={list.id} list={list} id={list.id} />
    )}</div>
  )
}

/组件/项目.jsx

import React from 'react'
import { connect } from 'react-redux'
import Editor from './Editor'
import Item from './Item'
export default class Items extends React.Component {
  render () {
    const {items, onEdit, onDelete, onValueClick, isEditing} = this.props
    return (
      <ul className="items">{items.map(item =>
        <Item 
          className="item"
          key={item.id}
          id={item.id}
          isEditing={item.isEditing}>
          <Editor
            isEditing={item.isEditing}
            value={item.text}
            onValueClick={onValueClick.bind(null, item.id)}
            onEdit={onEdit.bind(null, item.id)}
            onDelete={onDelete.bind(null, item.id)}
          />
        </Item>
      )}</ul>
    )
  }
}
export default connect(
  state => ({
    items: state.items
  })
)(Items)

/组件/项目.jsx

import React from 'react'
export default class Item extends React.Component {
  render() {
    const { id, isEditing, ...props } = this.props
    return (
      <li {...props}>{props.children}</li>
    )
  }
}

/组件/App.jsx

class App extends React.Component {
  handleClick = () => {
    this.props.dispatch(createList({title: "New Shopping List"}))
  }
  render() {
    const lists = this.props.lists
    return (
      <div>
        <button 
          className="add-list"
          onClick={this.handleClick}>Add Shopping List</button>
        <Lists lists={lists}/>
      </div>
    )
  } 
}
export default connect(state => ({ lists: state.lists }))(App)

假设这一切都意味着一次对单个数组进行操作,那么这部分看起来相当可疑:

case types.CREATE_LIST:
  return [
    ...state,
    {
      id: action.id,
      title: action.title,
      items: action.items || []
    }
  ]

...state正在将现有的任何数组扩展到您要返回的新数组中,听起来这并不是您真正想要的行为。我的第一个猜测是,当你创建一个新列表时,你可能只想返回里面的一个新项目,而不是整个旧列表内容加上这个新项目。

其他一些不可变样式的更新代码看起来也有点复杂。我知道"在数组中间更新一个东西"并不总是容易处理的。你可能想看看这篇关于更新不可变数据的SO文章,它列出了几种方法。我还有一个链接回购,它对Redux相关的库进行了编目,它有一个不可变数据管理库列表,可能会让你更容易。

尽管@markerikson的提示是我遇到的问题的一部分,但它并没有完全解决问题。我不得不把我的mapStateToProps固定到这个上

function mapStateToProps(state, props) {
  return {
    lists: state.lists,
    listItems: props.list.items.map(id => state.items[
        state.items.findIndex(item => item.id === id)
      ]).filter(item => item)
  }
}

并从以前的实现中删除我的Items.jsx 中单独项目的连接