React Native:复选框列表结构

React Native: Checkbox List Structure

一个 user 对象有一个引用一个或多个学校对象的数组属性 schools。我想使用 <List><CheckBox> 来改变 schools 数组。

我将 user 对象加载到视图中,并加载 listOfSchools(从应用程序状态)以生成复选框列表:

<List data={listOfSchools} keyExtractor={ item=> item._id } renderItem={({item})=>renderItem(item)} />

renderItem函数:

const renderItem = (school) => {

    return <ListItem
        title={school.name}
        accessory={()=>renderAccessory(school)}
    />
};

renderAccessory函数:

const renderAccessory = (school) => {
    return <CheckBox checked={() => checkSchool(school._id)} onChange={()=>changeSchool(school._id)} />
}

如果 user.schools 数组中引用了 school._idcheckSchool 函数 returns 布尔值。 changeSchool 函数在 users.schools 数组中添加或删除 school._id

changeSchool函数:

const changeSchool = (schoolId) => {
    let checked = checkSchool(schoolId);
    if (!checked) {
        // add schoolId to user.schools
    } else {
        // remove schoolId from user.schools
    }
}

这根本行不通。看来无论我用什么来改变状态,复选框都不会更新,user.schools 数组也不会改变。

构建这样一个设计目标的正确方法是什么?

假设您使用 UI Kitten,我可以看出您的 CheckBox 组件的 checked 属性值错误。

UI Kitten CheckBox reference

checked prop 需要是 boolean 而不是你那里的 Callable

我会尝试像这样更改代码:

const renderAccessory = (school) => {
    const isChecked = checkSchool(school._id);
    return <CheckBox checked={isChecked} onChange={()=>changeSchool(school._id)} />
}

如果有帮助请告诉我。

在尝试各种解决方案时,我可以在这里得出一些结论:

  • 使用@Cornel Raiu 给出的解决方案,已正确计算已选中和未选中的标志,但是,在 checked/unchecked
  • 的状态下显示不正确
  • 我用 Toggle 替换了 Checkbox,只是为了确保它也适用于 iOS
  • 我仍然面临的问题是,即使被切换的项目状态正确填充它正在重置
  • Toggles的外部容器是List和ListItem,
  • 观察到 List 上的 Press 事件实际上使 Checkbox/Toggle 进入正确的显示状态...

解决方案: 经过更长时间的研究和实验,我通过以下方法得到了我的东西 -

  • 我维护了单独的托运项目集合

  • 已存在主条目集合状态,作为列表的输入

  • 每次点击 Checkbox/Toggle,数据的主列表被克隆并复制回其状态

  • 这触发了组件的轻微重新渲染,一切正常。

      const [cashTransactions, setCashTransactions] = useState([]); // master data
      const [selectedTransactions, setSelectedTransactions] = useState([]); // selected data
      const renderItem = ({ item, index }) => (
              <ListItem
                title={'('+item.id + ') ' + item.firstName + ' ' + item.lastName}
                description={Moment(item.createdOn).format('yyyy-MM-DD hh:mm:ss')}
                accessoryLeft={selectedTransactions.includes(item.id) ? RadioOnIcon : RadioOffIcon}
                accessoryRight={() => checkBoxSpace(item)}
              />
            );
    
    
            const checkBoxSpace = (item) => {
              let itemChecked = selectedTransactions.includes(item.id);
              return (
                <View style={styles.actionContainer}>
                  <Button size='tiny' status='basic' accessoryLeft={rupeeSymbol}>{item.amount}</Button>
                  <Toggle checked={itemChecked} status='primary' size='small' onChange={checked => checkboxChecked(item, checked)}></Toggle>
                </View>
              )
            }
    
    
            const checkboxChecked = (item, checked) => {
              console.log('Item -' + item.id + ' ' + checked);
              if (checked) {
                if (!selectedTransactions.includes(item.id)) {
                  selectedTransactions.push(item.id);
                }
              } else {
                if (selectedTransactions.includes(item.id)) {
                  selectedTransactions.pop(item.id);
                }
              }
              console.log('selectedTransactions ' + JSON.stringify(selectedTransactions));
              // This is the thing i applied to get it done.
              const cloned = [...cashTransactions];
              setCashTransactions(cloned);
            }
    
                   // View
                    <List
                        style={styles.container}
                        data={cashTransactions}
                        renderItem={renderItem}
                      />