5

I have a screen where the user is able to enable/disable specific push notifications. When a user presses on a toggle the correct toggle is changed and the state is being updated.

The issue that I have is that after the switch happened it does some strange ordering. See the video here (I'm sorry about the video quality, tried to execute it with this command: xcrun simctl io booted recordVideo toggle.mp4).

Component state

state = {
    defaultNotifications: {
      config_error: { active: true, normalized: 'Notifiy on configuration error' },
      on_first_start: { active: true, normalized: 'Notifiy on first start' },
      on_trigger: { active: true, normalized: 'Notifiy on trigger' },
      on_panic_start: { active: true, normalized: 'Notifiy on panic start' },
      on_panic_end: { active: true, normalized: 'Notifiy on panic sell end' },
      order_placed: { active: true, normalized: 'Notifiy on order placed' },
      trade_completed: { active: true, normalized: 'Notifiy on trade completed' },
      order_cancelled: { active: true, normalized: 'Notifiy on order cancelled' },
    }
  }

Toggle function

const enabledNotifications = [];
const stateNotifications = this.state.defaultNotifications;
Object.keys(stateNotifications).forEach((notification) => {
  if (stateNotifications[notification].active) {
    enabledNotifications.push(notification);
  }
});

I needed a comma separated string with the 'active' notification names for a POST request later:

const requestBody = qs.stringify({
  player_id: this.state.playerId,
  permissions: enabledNotifications.toString()
}, { arrayFormat: 'comma', encode: false });

Change the toggle to !active

toggleNotification = (notification) => {
    this.setState({ defaultNotifications: {
      ...this.state.defaultNotifications,
      [notification]: {
        ...this.state.defaultNotifications[notification],
        active: !this.state.defaultNotifications[notification].active,
      }
    } });
  };

Toggle in JSX

    const userNotifications = this.state.defaultNotifications;

    return (
      Object.keys(userNotifications).map((notification) =>
        <ListItem
        key={notification}
        >
          <Image
            style={{ width: 24, height: 24 }}
            source={require('../../../assets/more_icons/notify_green.png')}
          />
          <Text style={styles.notificationText}>
            { userNotifications[notification].normalized }
          </Text>
          <Switch
             onValueChange={() => this.toggleNotification(notification)}
             value={userNotifications[notification].active}
             style={{ marginLeft: 'auto' }}
           />
        </ListItem>
      )
    );

I remember that I did clear my XCode Simulator cache with xcrun simctl erase all, a few minutes before this error occurred. But I can't think of any reason why that would cause any related issues.

Kevin Etore
  • 856
  • 1
  • 10
  • 27
  • Hello Kindly follow my answers here it will help you to achieve same https://stackoverflow.com/questions/53368506/react-native-how-to-use-flatlist-to-create-multiple-switches/53368576#53368576 – Hardik Virani Jun 06 '19 at 12:42

1 Answers1

2

JavaScript does not guarantee the order of properties when iterating through an objects's properties. See this answer. So you cannot be sure that the keys are returned in the way you anticipate right now, and that can lead to random changes while rendering.

So my recommendation is to order the object keys actively prior to displaying them. For example, to sort them alphabetically:

Object.keys(userNotifications).sort().map((notification) => ...

Alternatively, you could use an ordered array you set yourself:

['config_error', 'on_first_start', 'on_trigger', 'on_panic_start', 'on_panic_end', 'order_placed', 'trade_completed', 'order_cancelled'].map(key => userNotifications[key]).map((notification) => ...
fjc
  • 4,247
  • 12
  • 30