0

I am attempting to have an icon switch its visual when clicked (like a checkbox). Normally in react native I would do something like this:

const [checkbox, setCheckbox] = React.useState(false);
...
<TouchableHighlight underlayColor="transparent" onPress={() => {setCheckbox(!setCheckbox)}}> 
    {added ? <MaterialIcons  name="playlist-add-check" size={40} />
    : <MaterialIcons  name="playlist-add" size={40} />}
</TouchableHighlight>

However I have made some changes, and now I can't seem to replicate this behavior. I am using AsyncStorage class to storage and get arrays of objects for display. For simplification, in the example below I removed the storage code, and the objects each have an 'id' and an 'added' attribute, which is essentially the boolean value of the checkbox.

I am now attempting to update the icon shown to the user whenever it is pressed. I know the function is being called, but it will not update the icon. I am using array.map to create the list of icons. I created a demo here, and the code is below: https://snack.expo.dev/@figbar/array-map-icon-update

const templateObject = {
  id: 0,
  added: false,
};
const templateObject2 = {
  id: 1,
  added: true,
};
export default function App() {
  const [savedNumbers, setSavedNumbers] = React.useState([]);

  React.useEffect(() => {
    setSavedNumbers([templateObject,templateObject2]);
  }, []);

  const populateSavedNumbers = () =>
    savedNumbers.map((num, index) => <View key={index}>{renderPanel(num.id,num.added)}</View>);
  const updateNumber = (id) => {
    let capturedIndex = -1;
    for(var i = 0; i < savedNumbers.length; i += 1) {
        if(savedNumbers[i].id === id) {
          capturedIndex = i;
          break;
        }
    }
    let _tempArray = savedNumbers;
    _tempArray[capturedIndex].added = !_tempArray[capturedIndex].added;
    setSavedNumbers(_tempArray);
  }
  const renderPanel = (id:number, added:boolean) => {
    return (
        <View>
          <TouchableHighlight underlayColor="transparent" onPress={() => {updateNumber(id);}}> 
              {added ? <MaterialIcons  name="playlist-add-check" size={40} />
              : <MaterialIcons  name="playlist-add" size={40} />}
           </TouchableHighlight>
        </View>
    );
  }
  return (
    <View>
    <View>buttons:</View>
      <View>{populateSavedNumbers()}</View>
    </View>
  );
}

1 Answer 1

1

This is a common React pitfall where things don't re-render when it seems like they should. React does shallow comparisons between new and old states to decide whether or not to trigger a re-render. This means that, when declaring a variable to simply equal a state variable which is an object or an array, a re-render is not triggered since those two variables now reference the same underlying data structure.

In this case, you are setting _tempArray to reference the array savedNumbers rather than creating a new array. Therefore, React's shallow comparison comes back as "equal", and it doesn't believe that a re-render is necessary.

To fix this, change this line:

let _tempArray = savedNumbers;

to this:

let _tempArray = [...savedNumbers];
Sign up to request clarification or add additional context in comments.

Comments

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.