Reactjs - 使用复选框

时间:2018-06-15 14:07:29

标签: javascript reactjs redux

Prerquisite

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

目标

在阵列中添加/删除帐户&选中/取消选中复选框时存储(redux):

    检查
  • checbox - >将帐户添加到数组&存储
  • 复选框未选中 - >从阵列中删除帐户&存储

逻辑

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

  • 一个用于管理复选框状态
  • 一个管理添加/删除帐户到阵列& 存储

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

我的代码

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

1 个答案:

答案 0 :(得分:1)

我认为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)
}}

First toggleChange:

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

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

    this.props.toggleCheckbox(this.state.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的当前值(由于async setState,它可能是旧的)。您更新它的.accounts属性,然后将整个内容(包括.accounts.checkboxes)发送到this.setState

由于.checkboxes状态可能已经过时(之前的this.setState可能尚未解雇),这会将旧的.checkboxes状态排队,以覆盖您尝试保存在{的新状态。 {1}}。

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

  

因为setState是异步的,所以后续调用在同一个更新中   循环将覆盖以前的更新,并且之前的更改将会   迷路了。

Beware: React setState is asynchronous!

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

当然,这取决于你的应用需求,所以为了举例,我会做一些假设......

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

在这种情况下,我会通过this.state传入选定帐户的列表。

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

在组件的渲染功能中,我会根据帐户是否存在于“选定帐户”列表中来计算是否应选中该复选框。如果它存在,则检查它。如果没有,不检查。

然后,当用户点击选中/取消选中该框时,我会调度该功能,以便从“选定的帐户”列表中添加或删除该帐户,就是这样。该帐户将被添加或删除,这将导致您的道具更新,这将重新呈现您的组件,这将选中或取消选中相应的框。

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