1

I have a few components being rendered from an Array.map() that won't update even when the state (pertaining to said array) changes, and I have no clue why this is.

Home (main component) :

import React, { useState } from 'react';
import { View, TouchableOpacity } from 'react-native';
import { base } from './data';

import Indicator from './components/Indicator';

const Home = () => {
  const [switches, setSwitches] = useState(base);

  const handleToggle = (id: number) => {
    base.map(switch => {
      switch.on =
        (switch.on && switch.id !== id) || (!switch.on && switch.id === id)
          ? !switch.on
          : switch.on;
    });
    setSwitches(base);
  };

  return (
    <View>
      {switches.map(switch => (
        <TouchableOpacity
          onPress={() => handleToggle(switch.id)}
          key={switch.id}
        >
          <Indicator
            color={switch.color}
            on={switch.on}
          />
        </TouchableOpacity>
      ))}
    </View>

Indicator component:

import React from 'react';
import { View } from 'react-native';

import { On, Off } from './Indicators';

interface IndicatorProps {
  color: string;
  on: boolean;
}

const Indicator: React.FC<IndicatorProps> = ({ color, on }) => {
  return (
    <View>
      {on ? <On /> : <Off />}
    </View>
  );
};

export default Indicator;

I have verified that the state of switches is changing as expected when clicking the touchable areas, but no visible change occurs in the components.

2 Answers 2

1

You need to setState with an entirely new object so that it does not pass a shallow comparison.

You can do something like:

const handleToggle = (id: number) => {
  const newState = switches.map(switch => ({
    ...switch,
    on: (switch.on && switch.id !== id) || (!switch.on && switch.id === id) 
      ? !switch.on
      : switch.on
  }));
  setSwitches(newState);
};
Sign up to request clarification or add additional context in comments.

1 Comment

Got it, thanks. I'm not sure why, but when I tried to do it with the ternary ... it didn't work, must have missed something. This works just fine though :)
0

I have verified that the state of switches is changing as expected

Are you 100% sure about this?

    base.map(switch => {
      switch.on =
        (switch.on && switch.id !== id) || (!switch.on && switch.id === id)
          ? !switch.on
          : switch.on;
    });
    setSwitches(base);

This code doesn't toggle the switch value. map call calculates and returns the transformed array, but doesn't change it in place, and you then throw away the result, before calling setSwitches every time with exact same untoggled base value. In addition, you're toggling using base, when it looks like you probably want to toggle the state of switches.

You want something more like this:

    setSwitches(switches.map(switch => {
      switch.on =
        (switch.on && switch.id !== id) || (!switch.on && switch.id === id)
          ? !switch.on
          : switch.on;
    }));

3 Comments

I thought it wouldn't either, but yes, I am 100% certain that the state is being updated.
Does the fixed code here work for you? There's a few different issues interacting (you're mutating the switch object too, for example, which might indeed change the state without affecting the react state) but I think this should work.
I had tried it previously, but it didn't work either.

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.