2016-04-03 66 views
0

我一直在与这个争斗太久,有人能指引我走向正确的方向吗? 问题:当我创建一个列表时,我可以更新/删除它。我也可以添加项目并更新/删除这些项目。当我添加另一个列表时,来自前者的项目会被转到后者,然后我无法编辑这些项目。如果我删除列表并且不刷新浏览器,这些项目仍然在列表中。我需要一种方式将两者结合在一起,使列表只知道它的项目 预先感谢您的帮助。React Redux将项目添加到列表转到其他创建的列表。

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

/组件/ 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) 

回答

0

尽管@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

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的列表,可能会让你更容易。

+0

这是问题的一部分。感谢您指出。 – Diego