1

A user object has an array prop schools that references one or more school objects. I would like to use a <List> with <CheckBox> to mutate the schools array.

I load the user object into the view, and I load the listOfSchools (from the application state) to generate the checkbox list:

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

The renderItem function:

const renderItem = (school) => {

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

The renderAccessory function:

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

The checkSchool function returns boolean on if the school._id is referenced in the user.schools array. The changeSchool function adds or removes the school._id from the users.schools array.

The changeSchool function:

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

This drastically does not work. It appears that no matter what I use to mutate the state, the checkboxes never update, nor does the user.schools array mutate.

What is the proper way to structure such a design goal?

6
  • 1
    I assume you use React Native Elements. If that is the case checked needs to be a boolean Commented May 1, 2020 at 19:45
  • To clarify, I'm using ui-kitten, which seems to be mostly the same. onChange is an available prop. @CornelRaiu, checkSchool returns a boolean. Is that acceptable? Commented May 1, 2020 at 20:05
  • 1
    I see. I will have a look then Commented May 1, 2020 at 20:06
  • 1
    so it works ok now? I was currently just updating my answer Commented May 1, 2020 at 20:16
  • 1
    It appears that this was the major issue, yes. If you post your answer, I will accept it Commented May 1, 2020 at 20:17

2 Answers 2

1

Assuming that you use UI Kitten, I can see that you got the checked prop value wrong for the CheckBox component.

UI Kitten CheckBox reference

The checked prop needs to be a boolean not a Callable as you have it there

I would try to change the code like this:

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

Let me know if that helped.

Sign up to request clarification or add additional context in comments.

Comments

0

While trying various solutions i can conclude few things here:

  • With the solution given by @Cornel Raiu the checked and unchecked flags are getting correctly calculated however, the display was not correct with the state of checked/unchecked
  • I replaced Checkbox with Toggle, just to be sure that it works with iOS too
  • PROBLEM that i faced still is that, even the State of item getting toggled is correctly populating it was getting reset
  • The outside container of Toggles is List and ListItem,
  • OBSERVATION is that the Press event on List was actually getting the Checkbox/Toggle into correct Display State...

SOLUTION: After longer time of research and experiments I got my thing working with following approach -

  • I maintained separate collection of Checked Items

  • There is already a state of Collection of master items, as input to List

  • Every time the Checkbox/Toggle is clicked, the master list of Data is cloned and copied back to its state

  • This was triggering the slight re-render of component and thing is working as expected.

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

1 Comment

I have used UI-Kitten Library

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.