首页 > 解决方案 > Reactjs - 从数组中添加/删除项目并使用复选框存储

问题描述

先决条件

我正在获取在页面加载时显示的帐户列表(Ajax 请求)(旁边有一个复选框)。默认情况下,所有帐户都被选中并添加到商店(redux)。

目标

选中/取消选中复选框时,从数组和存储(redux)中添加/删除帐户:

逻辑

我创建了两个单独的动作和减速器:

在测试我的代码时,它一开始可以正常工作,但最终添加/删除的帐户不正确。问题一定出在 saveAccount() 但不确定我做错了什么?

我的代码

在 ComponentWillMount() 中将数据预填充到存储中:

  componentWillMount = () => {
    let defaultAccount = this.props.account
    let defaultCheckbox = this.props.checkboxStatus

    for(let i =0; i < this.props.products.arrangements.items.length; i++){
      const data = {}
      data['id'] = i
      data['isSelected'] = true
      data['sortCode'] = this.props.products.arrangements.items[i].sortCode
      data['accountNumber'] = this.props.products.arrangements.items[i].accountNumber
      data['accountName'] = this.props.products.arrangements.items[i].accountName
      defaultAccount = defaultAccount.concat(data)

      const checkboxesArray = {}
      checkboxesArray['id'] = i
      checkboxesArray['checked'] = true
      defaultCheckbox = defaultCheckbox.concat(checkboxesArray)
    }

    this.props.addAccount(defaultAccount)
    this.props.toggleCheckbox(defaultCheckbox)
  }

显示来自 Ajax 响应的帐户列表 (this.props.products.arrangements.items)

render() {
  return (
    <div>
      {typeof(this.props.products.arrangements.items) !== 'undefined' &&
       (Object.keys(this.props.account).length > 0) &&
       (typeof(this.props.checkboxStatus) !== 'undefined') &&
       (Object.keys(this.props.checkboxStatus).length > 0) &&
       (Object.keys(this.props.products.arrangements.items).length > 0) &&
        <div>
          {this.props.products.arrangements.items.map((item,i) =>
            <div className="accountContainer" key={i}>
              <Checkbox
                required
                label={"Account Number "+item.accountNumber+" Product Name "+item.accountName}
                value={true}
                checked={this.props.checkboxStatus[i].checked === true? true: false}
                onChange = {(e) => {
                  this.toggleChange(this.props.checkboxStatus[i])
                  this.saveAccount(e, i, item.accountNumber, item.accountName)
                }}
              />
                </div>
          )}
        </div>
      }
      </div>
  )
}

选中/取消选中复选框时更新 isSelected 值:

saveAccount = (e, i, accountNumber, productName) => {
  const data = {};
  data['id'] = i
  data['accountNumber'] = accountNumber
  data['productName'] = productName

  if(this.props.checkboxStatus[i].checked === true){
    let accountArray = Array.from(this.props.account)
    accountArray[i].isSelected = true
    this.props.addAccount(accountArray)
  }
  else {
    let accountArray = Array.from(this.props.account)
    accountArray[i].isSelected = false
    this.props.addAccount(accountArray)
  }
}

减速器

function Eligible(state = { products: {}, account: [], checkboxStatus: [] }, action){
  switch (action.type){
    case ADD_PRODUCTS:
      return {
        ...state,
        products: action.data
      }
    case ADD_ACCOUNT:
      return {
        ...state,
        account: action.data
      }
    case TOGGLE_CHECKBOX:
      return {
        ...state,
        checkboxStatus: action.data
      }
    default:
      return state
  }
}

行动

export const ADD_PRODUCTS = 'ADD_PRODUCTS'
export const ADD_ACCOUNT = 'ADD_ACCOUNT'
export const TOGGLE_CHECKBOX = 'TOGGLE_CHECKBOX'

export function addProducts(data){
  return {type: ADD_PRODUCTS, data}
}

export function addAccount(data) {
  return { type: ADD_ACCOUNT, data}
}

export function toggleCheckbox(data) {
  return { type: TOGGLE_CHECKBOX, data}
}

更新复选框状态:

  toggleChange = (checkbox) => {
    let toggleCheckbox = this.props.checkboxStatus
    toggleCheckbox[checkbox.id].checked = !checkbox.checked
    this.props.toggleCheckbox(toggleCheckbox)
  }

标签: javascriptreactjsredux

解决方案


我认为 的异步性this.setState可能会导致问题。

this.state包含accountscheckboxes

this.state = {
  accounts: [],
  checkboxes: []
}

在您的更改事件处理程序中,您调用两个函数:

onChange = {(e) => {
    this.toggleChange(this.props.checkboxStatus[i])
    this.saveAccount(e, i, item.accountNumber, item.accountName)
}}

第一个切换更改:

toggleChange = (checkbox) => {
    let toggleCheckbox = [...this.state.checkboxes];

    toggleCheckbox[checkbox.id].checked = !checkbox.checked
    this.setState({
        checkboxes: toggleCheckbox
    })

    this.props.toggleCheckbox(this.state.checkboxes)
}

您正在更新状态的 checkboxes 属性(通过 this.setState) - 一切都很好。但在最后一行,你昏倒this.state.checkboxes了。由于 this.setState 是异步的,这可能不会反映您刚刚所做的更改(您可以toggleCheckbox改为发送)。

事件处理程序中调用的下一个函数是 saveAccount,它包含(部分):

const addAccountState = this.state

if(this.props.checkboxStatus[i].checked === true){
    addAccountState.accounts = addAccountState.accounts.concat(data)
    this.setState(addAccountState)
    this.props.addAccount(addAccountState.accounts)
}

在这里,您正在获取 this.state 的当前值(由于异步 setState,它可能是旧的)。您更新它的 .accounts 属性,然后将整个内容(包括.accountsand .checkboxes)发送到this.setState.

由于.checkboxes状态可能是旧的(前一个this.setState可能尚未触发),这会将旧的 .checkboxes 状态排队以覆盖您尝试保存的新状态toggleChange()

可以使用快速而肮脏的修复程序this.setState({accounts: addAccountState.accounts}),但也可能存在其他问题(例如this.state直接修改属性)。

因为 setState 是异步的,所以在同一个更新周期的后续调用会覆盖之前的更新,之前的修改会丢失。

注意:React setState 是异步的!


关于商店和状态的分离......一种选择可能是根本不单独存储复选框,而是根据选择的帐户来计算它们。

当然,这取决于您的应用程序的需求,因此我将做一些假设以作为示例......

  • 您的应用程序需要一个选定帐户的列表
  • 您的组件需要显示所有帐户的列表
  • 您的组件对每个帐户都有复选框:选中 = 选定 = 应用程序“选定帐户”列表的一部分。

在这种情况下,我将通过 传入选定帐户的列表props

在组件中,我将拥有本地状态中所有帐户的列表(如果您的“所有帐户”列表已经通过道具传递,那么只需使用它 - 不需要本地状态)。

在组件的渲染函数中,我将根据帐户是否存在于“选定帐户”列表中来计算是否应选中复选框。如果存在,则对其进行检查。如果不是,则不检查。

然后,当用户单击以选中/取消选中该框时,我将调度该函数以从“选定帐户”列表中添加或删除该帐户,仅此而已。该帐户将被添加或删除,这将导致您的道具更新,这将重新渲染您的组件,这将根据需要选中或取消选中这些框。

这可能不完全符合您特定应用程序的需求,但我希望它能给您一些想法!:)


推荐阅读