shahen94 / react-native-switch

Customisable switch component for RN
MIT License
296 stars 172 forks source link

Switch not visually toggling in drawer navigator but state is changing #105

Open jianrontan opened 1 year ago

jianrontan commented 1 year ago

So in my Drawer.Navigator is a page called settings.js that manages state for my switch, SettingsComponent.jsx that uses Switch from 'react-native-switch'. The problem is that when I press the switch, it doesn't visually toggle, but according to console.log, the state is actually changing.

Before I implemented the state in settings.js, it used to be able to toggled.

I tried every trick in the book (I believe), using 'useDrawerStatus', 'useCallback', 'useEffect' to re-render, but nothing seems to want to make my switch visually move!

For more context, settings.js is a Drawer.Screen in my Drawer.Navigator and I believe this is to be the issue as I believe the switch is able to visually toggle if it were in my Stack.Navigator instead as previously, I have encountered the same problem but with the native 'Switch' component from 'react-native' and just by moving the component to my Stack.Screen, it ends up being able to visually toggle.

settings.js

import React, { useState, useCallback, useEffect } from 'react';
import { View, ScrollView, SafeAreaView } from 'react-native';
import { useNavigation, useFocusEffect } from '@react-navigation/native';

import SettingsComponent from '../myComponents/drawer/settings/SettingsComponent';
import { COLORS, icons, images, FONT, SIZES } from '../constants';

function Settings({navigation}) {
  const [isBreakContinueEnabled, setBreakContinueEnabled] = useState(false);
  const [isBreakSaveEnabled, setBreakSaveEnabled] = useState(false);
  const [isNotificationEnabled, setNotificationEnabled] = useState(false);

  const setBreakContinueEnabledMemoized = useCallback((value) => {
    setBreakContinueEnabled(value);
  }, []);

  const setBreakSaveEnabledMemoized = useCallback((value) => {
    setBreakSaveEnabled(value);
  }, []);

  const setNotificationEnabledMemoized = useCallback((value) => {
    setNotificationEnabled(value);
  }, []);

  useEffect(() => {
  }, [isBreakContinueEnabled, isBreakSaveEnabled, isNotificationEnabled]);

  console.log({ isBreakContinueEnabled, isBreakSaveEnabled, isNotificationEnabled });

  const breakContinue=[{title: 'Continue after break', subTitle:'Stopwatch starts once break ends'}];
  const breakSave=[{title: 'Save break time', subTitle:'Save break time for later on continue'}];
  const notificationToggle=[{title: 'Notifications', subTitle: 'Notification when break ends'}];

  return (
    <SafeAreaView style={{ flex: 1, backgroundColor: COLORS.grayBeige }}>
      <ScrollView showsVerticalScrollIndicator={false}>
        <View>
          <SettingsComponent 
            settingsOptions={breakContinue} 
            isEnabled={isBreakContinueEnabled} 
            setIsEnabled={setBreakContinueEnabledMemoized} 
          />
          <SettingsComponent 
            settingsOptions={breakSave} 
            isEnabled={isBreakSaveEnabled} 
            setIsEnabled={setBreakSaveEnabledMemoized} 
          />
          <SettingsComponent 
            settingsOptions={notificationToggle} 
            isEnabled={isNotificationEnabled} 
            setIsEnabled={setNotificationEnabledMemoized} 
          />
        </View>
      </ScrollView>
    </SafeAreaView>
  );
}

export default Settings;

SettingsComponent.jsx

import { useState } from 'react';
import { View, Text, SafeAreaView, ScrollView } from 'react-native';
import { TouchableOpacity } from 'react-native-gesture-handler';
import { Switch } from 'react-native-switch';
import { useDrawerStatus } from '@react-navigation/drawer';

import { COLORS, FONT, SIZES } from '../../../constants';

const SettingsComponent = ({settingsOptions, isEnabled, setIsEnabled}) => {
    const isDrawerOpen = useDrawerStatus() === 'open';

    const toggleSwitch = () => {
        if (!isDrawerOpen){
          setIsEnabled(previousState => !previousState);
        }
      }

    return (
        <SafeAreaView>
            <View style={{ flex: 1, flexDirection: 'row', justifyContent: 'space-between' }}>
                {settingsOptions.map(({title, subTitle }, index) =>
                    <TouchableOpacity key={title}>
                        <View style={{
                            paddingHorizontal: 20,
                            paddingTop: 20,
                            paddingBottom: 20,
                        }}>
                            <Text style={{fontFamily: FONT.medium, fontSize: SIZES.medium, color: COLORS.themeColor}}>{title}</Text>
                            {subTitle && <Text style={{fontFamily: FONT.regular, fontSize: SIZES.small, color: COLORS.lightThemeColor}}>{subTitle}</Text>}
                        </View>
                    </TouchableOpacity>
                )}
                <View 
                    style={{
                        paddingHorizontal: 20,
                        paddingTop: 20,
                        paddingBottom: 20,
                        justifyContent: 'center',
                    }}
                >
                    <Switch
                        value={isEnabled}
                        onValueChange={toggleSwitch}
                        disabled={false}
                        activeText={'On'}
                        inActiveText={'Off'}
                        circleSize={25}
                        barHeight={20}
                        circleBorderWidth={1}
                        circleBorderActiveColor={COLORS.themeColor}
                        circleBorderInactiveColor={'#c9beb1'}
                        backgroundActive={COLORS.themeColor}
                        backgroundInactive={'#c9beb1'}
                        circleActiveColor={COLORS.lightBeige}
                        circleInActiveColor={COLORS.lightBeige}
                        changeValueImmediately={true} // if rendering inside circle, change state immediately or wait for animation to complete
                        innerCircleStyle={{ alignItems: "center", justifyContent: "center" }} // style for inner animated circle for what you (may) be rendering inside the circle
                        outerCircleStyle={{}} // style for outer animated circle
                        renderActiveText={false}
                        renderInActiveText={false}
                        switchLeftPx={2} // denominator for logic when sliding to TRUE position. Higher number = more space from RIGHT of the circle to END of the slider
                        switchRightPx={2} // denominator for logic when sliding to FALSE position. Higher number = more space from LEFT of the circle to BEGINNING of the slider
                        switchWidthMultiplier={2} // multiplied by the `circleSize` prop to calculate total width of the Switch
                        switchBorderRadius={20} // Sets the border Radius of the switch slider. If unset, it remains the circleSize.
                    />
                </View>
            </View>
        </SafeAreaView>
    );
};

export default SettingsComponent;
bektigalan commented 1 year ago

this happened to me too today, any progress how to fix it?

HoussamBk1993 commented 12 months ago

same problem here any ideas please ?