VicHofs
VicHofs

Reputation: 311

React Native: Array.map()-based rendered components not updating on state change

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.

Upvotes: 1

Views: 2954

Answers (2)

Tim Perry
Tim Perry

Reputation: 13216

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

Upvotes: 0

GitGitBoom
GitGitBoom

Reputation: 1912

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

Upvotes: 1

Related Questions