question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

useBottomSheetModal problem useCallback internal parameters that do not update

See original GitHub issue

Bug

Registrazione schermo 2020-11-20 alle 14 47 55

Hi @gorhom , i am having the following problem, i am using present by useBottomSheetModal, as you can see from the image through the buttons i should change the theme.

The status is not updated internally, only when you reopen the BottomSheetModal the status is updated.

How can I solve?

Environment info

Library Version
@gorhom/bottom-sheet 1.4.1
react-native 0.63.3
react-native-reanimated 1.13.2
react-native-gesture-handler 1.8.0

Reproducible sample code

import React, {
  useState,
  useEffect,
  useCallback,
  useMemo,
  useRef,
} from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { View, StyleSheet, Image, Dimensions } from 'react-native';
import { Text, Button, RadioButton } from 'react-native-paper';
import {
  Common,
  Fonts,
  Gutters,
  Layout,
  Images,
  Colors,
  ColorsDarkMode,
} from '@/Theme';
import { useTranslation } from 'react-i18next';
import Settings from '@/Store/Settings/Init';
const { width: SCREEN_WIDTH } = Dimensions.get('screen');

import AppIntroSlider from 'react-native-app-intro-slider';

import {
  BottomSheetModalProvider,
  useBottomSheetModal,
  BottomSheetOverlay,
} from '@gorhom/bottom-sheet';
import withModalProvider from './withModalProvider';
import BlurredBackground from './BlurredBackground';

const styles = StyleSheet.create({
  slide: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  image: {
    width: 320,
    height: 320,
    marginVertical: 32,
    resizeMode: 'stretch',
  },
  text: {
    color: 'rgba(255, 255, 255, 0.8)',
    textAlign: 'center',
  },
  title: {
    fontSize: 22,
    color: 'white',
    textAlign: 'center',
  },
  blurView: {
    ...StyleSheet.absoluteFillObject,
  },
  container: {
    ...StyleSheet.absoluteFillObject,
    borderTopLeftRadius: 10,
    borderTopRightRadius: 10,
    overflow: 'hidden',
  },
  androidContainer: {
    backgroundColor: 'rgba(255,255,255, 0.95)',
  },
  indicator: {
    alignSelf: 'center',
    width: (8 * SCREEN_WIDTH) / 100,
    height: 5,
    borderRadius: 4,
    backgroundColor: 'rgba(0, 0, 0, 0.5)',
  },
});

const IndexInstallationContainer = (props) => {
  const { t } = useTranslation();
  const dispatch = useDispatch();
  const settings = useSelector((state) => state.settings);
  const { present } = useBottomSheetModal();

  const handlePresentPress = useCallback(() => {
    present(
      <View style={{ flex: 1, backgroundColor }}>
        <Text>{settings.item.colorScheme}</Text>
        <RadioButton.Group
          onValueChange={(newValue) => changeButtonTheme(newValue)}
          value={settings.item.colorScheme}>
          <View style={(Layout.row, Layout.rowHCenter)}>
            <RadioButton value="light" />
            <Text>Light</Text>
          </View>
          <View style={(Layout.row, Layout.rowHCenter)}>
            <RadioButton value="dark" />
            <Text>Dark</Text>
          </View>
        </RadioButton.Group>
      </View>,
      {
        snapPoints: ['20%'],
        animationDuration: 300,
        overlayComponent: BottomSheetOverlay,
        overlayOpacity: 0.5,
        dismissOnOverlayPress: true,
        handleComponent: handle,
        //backgroundComponent: BlurredBackground,
        //onChange: handleChange,
      }
    );
  }, [present, dispatch, settings]);

  const slides = [
    {
      key: 'one',
      title: t('welcome'),
      text: '',
      image: Images.logo,
      backgroundColor: '#2196f3',
    },
    {
      key: 'two',
      title: t('change.language'),
      text: '',
      backgroundColor: '#ffc107',
    },
    {
      key: 'three',
      title: t('change.mode'),
      text: '',
      backgroundColor: '#4caf50',
    },
  ];

  const changeTheme = () => {
    dispatch(
      Settings.action({
        colorScheme: settings.item.colorScheme === 'dark' ? 'ligth' : 'dark',
      })
    );
  };

  const changeButtonTheme = (colorScheme) => {
    dispatch(Settings.action({ colorScheme }));
  };

  const openBottom = () => setState((prev) => ({ ...prev, open: !prev.open }));

  const b = (
    <Button
      style={[Gutters.largeHMargin, Gutters.largeBMargin]}
      raised
      mode="contained"
      onPress={handlePresentPress}>
      {t('actions.change')}
    </Button>
  );

  const _renderItem = ({ item }) => {
    return (
      <View style={[styles.slide, { backgroundColor: item.backgroundColor }]}>
        {item.image && <Image source={item.image} style={styles.image} />}
        <Text style={styles.title}>{item.title}</Text>
        {item.key === 'three' && b}
        <Text style={styles.text}>{item.text}</Text>
      </View>
    );
  };

  const _onDone = () => {
    //console.log(props)
    props.navigation.navigate('Login');
  };

  const [state, setState] = useState({
    open: false,
  });

  const { open } = state;

  const backgroundColor =
    settings.item.colorScheme === 'dark'
      ? ColorsDarkMode.backgroundPrimary
      : Colors.backgroundPrimary;

  const handle = () => {
    return (
      <View
        style={{
          paddingHorizontal: 16,
          paddingVertical: 5,
          backgroundColor,
        }}>
        <View
          style={[
            styles.indicator,
            {
              backgroundColor:
                settings.item.colorScheme === 'light'
                  ? 'rgba(0, 0, 0, 0.25)'
                  : 'rgba(255, 255, 255, 0.25)',
            },
          ]}
        />
      </View>
    );
  };

  return (
    <>
      <AppIntroSlider
        renderItem={_renderItem}
        data={slides}
        onDone={_onDone}
        nextLabel={t('next')}
        doneLabel={t('done')}
      />
    </>
  );
};

export default withModalProvider(IndexInstallationContainer);

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:9 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
gorhomcommented, Nov 30, 2020

hi @Angelk90 , the solution would be to make the modal an uncontrolled component that updates parent when value change also it contain its own state. read more

here is a working example:

import React, { useCallback, useMemo, useState } from 'react';
import { View, StyleSheet, Alert, Text } from 'react-native';
import { useBottomSheetModal, BottomSheetOverlay } from '@gorhom/bottom-sheet';
import Button from './components/button';
import ContactListContainer from './components/contactListContainer';
import withModalProvider from './screens/withModalProvider';

const ThemeModal = ({ theme: _theme, onChange }) => {
  // state
  const [theme, setTheme] = useState(_theme);

  // styles
  const containerStyle = useMemo(
    () => ({
      flex: 1,
      backgroundColor: theme === 'light' ? 'white' : 'black',
    }),
    [theme]
  );

  // callbacks
  const handleSettingLightTheme = useCallback(() => {
    setTheme('light');

    if (onChange) {
      onChange('light');
    }
  }, [onChange]);
  const handleSettingDarkTheme = useCallback(() => {
    setTheme('dark');

    if (onChange) {
      onChange('dark');
    }
  }, [onChange]);

  return (
    <View style={containerStyle}>
      <Text style={{ fontSize: 34, color: '#34eb58' }}>{theme}</Text>
      <Button
        label="Light"
        style={styles.buttonContainer}
        onPress={handleSettingLightTheme}
      />
      <Button
        label="Dark"
        style={styles.buttonContainer}
        onPress={handleSettingDarkTheme}
      />
    </View>
  );
};

const OverlayExample = () => {
  // state=
  const [theme, setTheme] = useState('light');

  // hooks
  const { present } = useBottomSheetModal();

  // callbacks
  const handleThemeChange = useCallback((theme: string) => {
    setTheme(theme);
  }, []);

  const handlePresentPress = useCallback(() => {
    present(<ThemeModal theme={theme} onChange={handleThemeChange} />, {
      snapPoints: [300, '50%'],
      animationDuration: 250,
      overlayComponent: BottomSheetOverlay,
      overlayOpacity: 0.75,
      dismissOnOverlayPress: true,
    });
  }, [handleThemeChange, present, theme]);

  // renders
  return (
    <View style={styles.container}>
      <Text style={{ fontSize: 34 }}>{theme}</Text>
      <Button
        label="Present Modal"
        style={styles.buttonContainer}
        onPress={handlePresentPress}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 24,
  },
  buttonContainer: {
    marginBottom: 6,
  },
});

export default withModalProvider(OverlayExample);

1reaction
Angelk90commented, Nov 25, 2020

Hi @maxckelly , I believe we have to wait @gorhom to find out more.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Use 'gorhom/bottom-sheet' React Native, Hooks can only be ...
The onPress function for the TouchableOpacity seems to be a problem here. Use some state to show or hide the BottomModel accordingly
Read more >
React Hooks cheat sheet: Best practices with examples
If you find that useState / setState are not updating immediately, the answer is simple: they're just queues. React useState and setState don't...
Read more >
Scrollables | React Native Bottom Sheet - GitHub Pages
This library provides a pre-integrated virtualized lists that utilize an internal functionalities with the bottom sheet container to allow smooth panning ...
Read more >
Common use cases for useCallback? : r/reactjs - Reddit
I know useCallback is used for memoizing functions so that they ... is passed around it can become stale (internal variables are not...
Read more >
A Performant Interactive Bottom Sheet with Fully Configurable ...
To enable or disable user interaction with the sheet. ... Animated value to be used as a callback for the position node internally....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found