【问题标题】:React Redux adding items to list carries over to other created lists.React Redux 将项目添加到列表会延续到其他创建的列表。
【发布时间】:2016-04-03 03:06:46
【问题描述】:

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

/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
  }
}

/reducers/lists.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
  }
}

/reducers/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>
  )
}

/components/Items.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)

/components/Item.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>
    )
  }
}

/components/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)

【问题讨论】:

    标签: javascript reactjs redux react-redux


    【解决方案1】:

    假设这都是一次对单个数组进行操作,这部分看起来很可疑:

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

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

    您的其他一些不可变样式的更新代码看起来也有些复杂。我知道“更新数组中间的东西”并不总是那么容易处理。您可能想查看this SO post on updating immutable data,其中列出了几种处理方法。我还有一个链接存储库,它列出了与 Redux 相关的库,它有一个 immutable data management libraries 列表,这可能会让你更轻松。

    【讨论】:

      【解决方案2】:

      虽然@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 中的项目单独从以前的实现中删除连接

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多