0

私はこれと長い間戦ってきました。誰かが私を正しい方向に向けることができますか? 問題: リストを作成すると、リストを更新/削除できます。アイテムを追加したり、これらのアイテムを更新/削除したりすることもできます。別のリストを追加すると、前者のアイテムが後者に引き継がれ、アイテムを編集できなくなります。リストを削除してブラウザを更新しないと、アイテムはリストに残ります。リストがそのアイテムについてのみ知っている方法で2つを結び付ける方法が必要です事前に助けてくれてありがとう。

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

/コンポーネント/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)

/コンポーネント/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>
  )
}

/コンポーネント/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)

/コンポーネント/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)
4

2 に答える 2

1

これがすべて一度に 1 つのアレイで動作することを意図していると仮定すると、この部分はかなり怪しいように見えます。

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

これ...stateは、存在する既存の配列を、返される新しい配列に展開することであり、実際に必要な動作ではないように聞こえます。私の最初の推測では、新しいリストを作成するときは、古いリストの内容全体とその新しいアイテムではなく、内部の新しいアイテムを 1 つだけ返したいと思うでしょう。

他の不変スタイルの更新コードの一部も、複雑に見えます。「配列の途中にあるものを更新する」ことは、必ずしも簡単に処理できるとは限りません。不変データの更新に関するこの SO 投稿をご覧になることをお勧めします。これには、それにアプローチするいくつかの方法がリストされています。また、Redux 関連のライブラリをカタログ化したリンク リポジトリもあり、作業を容易にする不変のデータ管理ライブラリのリストがあります。

于 2016-04-03T03:59:47.723 に答える