NativeBase icon indicating copy to clipboard operation
NativeBase copied to clipboard

[Bug] Toast is not working within a Modal

Open JLLLinn opened this issue 7 years ago • 67 comments

react-native, react and native-base version

RN: 0.45.1 React:16.0.0-alpha.12 native-base: 2.1.5

Expected behaviour

Toast to be shown in react native Modal

Actual behaviour

The toast is not shown in react native Modal

Steps to reproduce (code snippet or screenshot)

Do Toast.show() in a Modal

Is the bug present in both ios and android or in any one of them?

No that I know of. This was working with RN on 0.44 and native-base on a lower version (I can't remember exactly which one, either 2.1.4 or 2.1.3)

JLLLinn avatar Jun 26 '17 07:06 JLLLinn

@JLLLinn Check the latest of NativeBase i.e, v2.2.0

shivrajkumar avatar Jul 03 '17 05:07 shivrajkumar

Was this verified that it was fixed? I think I'm experiencing it as well. My issue is that the Modal is covering the Toast so it isn't visible.

I'm on RN 0.46.4 and native base 2.2.1 and using the ios emulator.

Thanks!

laxgoalie392 avatar Aug 10 '17 02:08 laxgoalie392

Same problem. I'm on RN 0.46.1 and native base 2.2.1 and using the ios emulator.

vovanmozg avatar Aug 15 '17 08:08 vovanmozg

Exactly the same problem using RN 0.47 and NativeBase 2.2.1. Any news about it? thanks!

mdebo avatar Aug 24 '17 08:08 mdebo

The Toast still appears behind the Modal (from React Native):

toast behind modal

the code is basically this:

import React from 'react';
import {
  Modal,
  Toast,
  Text,
  View,
} from 'react-native';

export default class LoginScreen extends React.Component<Props, State> {
  state = {
    modalVisible: false,
  }

  onResetPassword() {
    Toast.show({
      text: message,
      duration: 2000,
      position: "top",
      textStyle: { textAlign: "center" },
    });
  }    

  render() {
    return (
      <View>
        <Modal
          animationType="slide"
          transparent={false}
          visible={this.state.modalVisible}
          >
          <View>...</View>
        </Modal>
      </View>
    )
  }
}

Any suggestions?

I see there's something called zIndex as Layout Props - React Native

Using:

$ yarn list --depth=0 | grep 'native-base@\| react-native@'
├─ [email protected]
├─ [email protected]

Can something be done to the ToastContainer.js? https://github.com/GeekyAnts/NativeBase/blob/master/src/basic/ToastContainer.js

gianpaj avatar Nov 21 '17 09:11 gianpaj

@gianpaj Looks like you are using React-Native-Seed boilerplate. Nice! We will check this issue

SupriyaKalghatgi avatar Nov 22 '17 05:11 SupriyaKalghatgi

I just want to add that I’m having this issue too with v 2.3.3

idrakimuhamad avatar Dec 13 '17 13:12 idrakimuhamad

FYI: this issue happens both on Android and iOS

gianpaj avatar Dec 13 '17 18:12 gianpaj

Still present on 2.3.5

supercamilo avatar Dec 29 '17 15:12 supercamilo

I think I'm just going to use the Toast from ant-design-mobile.

My bad. Ant-Design Toast doesn't work. It also appears under the react-native Modal

gianpaj avatar Feb 10 '18 12:02 gianpaj

@gianpaj I'm also using antd-mobile for the datepicker. Perhaps I should take a look at their toast implementation too.

idrakimuhamad avatar Feb 10 '18 12:02 idrakimuhamad

@gianpaj What was the purpose of sharing graphs for commit-activity?

SupriyaKalghatgi avatar Feb 12 '18 06:02 SupriyaKalghatgi

Any hotfix for now with v2.3.10 ?

ThomasLabstep avatar Apr 03 '18 08:04 ThomasLabstep

Any chance this will be fixed anytime soon?

AdrianMrn avatar Apr 03 '18 11:04 AdrianMrn

Сталкивался с этой проблемой недавно, решил следующим способом. Просто добавляем в Modal элемент Root, а в него весь контент который вам нужен

P.S. на английский переводить лень, главное ведь код

translation : Faced this problem recently, I decided in the following way. Just add the Root element to the Modal, and all the content you need is in it

import { Root, Container, Content } from 'native-base';

<Modal>
    <Root>
        <Container>
             <Content>
                  // .. modal content with toast
             </Content>
        </Container>
    </Root>
</Modal>

N9vn1njdf avatar Apr 23 '18 14:04 N9vn1njdf

@Zeratyll Hey! It doesn't work for me at all, the toast still appears behind the Modal.

doreentseng avatar May 10 '18 10:05 doreentseng

@doreentseng Tried @Zeratyll 's solution of putting <Root/> into <Modal/>, but it still didn't work. @gianpaj Also tried to put style : { zIndex: 1000 } into Toast.show(), no effect as well. All of the results are tested in a real iPad. Tried Modal component from both original react-native and react-native-community, both of them didn't work. Will native-base provide its own modal component? @SupriyaKalghatgi @shivrajkumar Dear collaborators, when can we get an fix?

michaellee8 avatar Jun 01 '18 01:06 michaellee8

checked this issue after merging PR https://github.com/GeekyAnts/NativeBase/pull/1700. Placing Root inside a modal component as suggested by @Zeratyll https://github.com/GeekyAnts/NativeBase/issues/985#issuecomment-383596951 is working after merging. See attached Gif.

Sample code

import React from 'react';
import { Modal } from "react-native";
import { createStackNavigator } from 'react-navigation';
import { Root, Container, Text, Header, Left, Body, Right, Title, Content, Button, Toast } from 'native-base';

class HomeScreen extends React.Component {

  state = { modalVisible: false }

  render() {
    return (
      <Container>
        <Header>
          <Left />
          <Body>
            <Title>Home screen</Title>
          </Body>
          <Right />
        </Header>

        <Content>
          <Button onPress={() => this.setState({ modalVisible: !this.state.modalVisible })} style={{ margin: 20 }}>
            <Text>Show modal</Text>
          </Button>
        </Content>

        <Modal
          animationType="slide"
          transparent={false}
          visible={this.state.modalVisible}>
          <Root>
            <Container style={{ padding: 20 }}>
              <Button style={{ margin: 20 }} onPress={() => Toast.show({
                text: 'Wrong password!',
                buttonText: 'Okay'
              })}>
                <Text>Show toast</Text>
              </Button>
              <Button style={{ margin: 20 }} onPress={() => this.setState({ modalVisible: !this.state.modalVisible })}>
                <Text>Hide modal</Text>
              </Button>
            </Container>
          </Root>
        </Modal>
      </Container>
    );
  }
}

const App = createStackNavigator({
  Home: {
    screen: HomeScreen
  },
}, {
    navigationOptions: {
      header: null
    }
  });

export default () => <Root>
  <App />
</Root>

Gif

toast-modal

Edit : As @gianpaj pointed out this may not be a viable option. Please see the below comment.

akhil-ga avatar Jun 20 '18 09:06 akhil-ga

Placing Root inside a modal component as suggested ... is working fine @akhil-geekyants

This is not ideal or neither practical.

The state of Modal(s) should not be at the Root of an application. It should be where the Modal needs to appear, i.e. inside a screen/component/container.

If we had to do the way you suggest, the state of the Modal needs to pass down all the way to the Component in need (via props or in order ways).

I have to say this is not a viable option for any decent size application.

I don't know at the moment how to solve this, but pretty sure that's not it. Happy to be corrected if I'm wrong.

gianpaj avatar Jun 20 '18 11:06 gianpaj

@gianpaj updated my comment.

akhil-ga avatar Jun 25 '18 13:06 akhil-ga

Also having this problem. The Root workaround does work but also has an unwanted side-effect that windowed (non-fullscreen) modals no longer appear vertically centered, and no JSX adjustments appear to help.

JeremyBradshaw7 avatar Jul 05 '18 10:07 JeremyBradshaw7

The above Root fix didn't work for me as I'd like it to, as it completely ruins the vertical centring of the Toast

lewisflude avatar Jul 30 '18 11:07 lewisflude

My workaround for modals.

scene.js

<Scene>
  <ModalComponent />
</Scene>

ModalComponent.js

import Toast from 'mypathto/Toast';
class ModalComponent Component {
  
    showToast() {
        Toast.show({
          text: 'modal toast',
          position: 'bottom',
          duration: 3000
        });
    }

    render() {
        return (
            <Modal>
                <View>
                    <Button onPress={()=>this.showToast()}><Text>Toast<Text></Button>

                    <Toast
                      ref={c => {
                        if (c) Toast.toastInstance = c;
                      }}
                    />

                </View>
            </Modal>
        )
    }
}

mypathto/Toast.js

import {
  Toast as ToastNB,
} from "native-base";

class Toast extends ToastNB {
}

export default Toast;

Aleksandern avatar Aug 08 '18 11:08 Aleksandern

Сталкивался с этой проблемой недавно, решил следующим способом. Просто добавляем в Modal элемент Root, а в него весь контент который вам нужен

P.S. на английский переводить лень, главное ведь код

translation : Faced this problem recently, I decided in the following way. Just add the Root element to the Modal, and all the content you need is in it

import { Root, Container, Content } from 'native-base';

<Modal>
    <Root>
        <Container>
             <Content>
                  // .. modal content with toast
             </Content>
        </Container>
    </Root>
</Modal>

Very good, it works with me

tmtung144 avatar Oct 10 '18 02:10 tmtung144

My workaround for modals.

scene.js

<Scene>
  <ModalComponent />
</Scene>

ModalComponent.js

import Toast from 'mypathto/Toast';
class ModalComponent Component {
  
    showToast() {
        Toast.show({
          text: 'modal toast',
          position: 'bottom',
          duration: 3000
        });
    }

    render() {
        return (
            <Modal>
                <View>
                    <Button onPress={()=>this.showToast()}><Text>Toast<Text></Button>

                    <Toast
                      ref={c => {
                        if (c) Toast.toastInstance = c;
                      }}
                    />

                </View>
            </Modal>
        )
    }
}

mypathto/Toast.js

import {
  Toast as ToastNB,
} from "native-base";

class Toast extends ToastNB {
}

export default Toast;

Works for me, Nice Solution

AonanLi avatar Dec 01 '18 23:12 AonanLi

My workaround for modals.

scene.js

<Scene>
  <ModalComponent />
</Scene>

ModalComponent.js

import Toast from 'mypathto/Toast';
class ModalComponent Component {
  
    showToast() {
        Toast.show({
          text: 'modal toast',
          position: 'bottom',
          duration: 3000
        });
    }

    render() {
        return (
            <Modal>
                <View>
                    <Button onPress={()=>this.showToast()}><Text>Toast<Text></Button>

                    <Toast
                      ref={c => {
                        if (c) Toast.toastInstance = c;
                      }}
                    />

                </View>
            </Modal>
        )
    }
}

mypathto/Toast.js

import {
  Toast as ToastNB,
} from "native-base";

class Toast extends ToastNB {
}

export default Toast;

It's necessary to extend the Toast? I tried the trick directly with NB export and it worked.

EDIT: Yes, it's necessary beacuse the ref to the root is the same for all present and future Toasts (Without extending the Toast, it will try to render within the modal even if it doesn't exist anymore).

filippoitaliano avatar Jan 14 '19 13:01 filippoitaliano

onPress={()=>this.setState({ toastVisible: !this.state.toastVisible },() =>Toast.show({ text: 'Wrong password!', buttonText: 'Okay' }))}

This code is working for me. Thanks @akhil-geekyants. This comment helped me https://github.com/GeekyAnts/NativeBase/issues/985#issuecomment-398688935

jayan2019 avatar Jan 24 '19 10:01 jayan2019

As mentioned in NB Docs https://docs.nativebase.io/Components.html#toast-def-headref - For Toast to work, you need to wrap your topmost component inside <Root> from native-base. ,the solution is to wrap Toast with Root within Modal, which has already been found in discussion above...

suvenduchhatoi avatar Feb 11 '19 07:02 suvenduchhatoi

Wrapping Modal content with Root solved the problem with z-indexing Toast, but this problem appears next https://github.com/GeekyAnts/NativeBase/issues/937

artshevtsov avatar Apr 19 '19 07:04 artshevtsov

In my case I use Modal as component and the Toast is being shown after executing an async function, this works for me:

Toast:

import { Toast as ToastNB } from "native-base";

class Toast extends ToastNB {
}

export default Toast;

Modal Component:

import Toast from 'path/toast';

const ModalContainer = ({
  visible, onClose, children, onShow,
}) => (
  <Modal
    visible={visible}
    onRequestClose={onClose}
    onShow={() => onShow(Toast)}
  >
    <View>
      {children}
    </View>
    <Toast
      ref={(t) => {
        Toast.toastInstance = t;
      }}
    />
  </Modal>
);

Usign the Modal Component:

import ModalContainer from 'path/modalContainer/;

class SomeClass Component {
  
    showToast() {
       add1(10).then(() =>  this.toastInstance.show({
          text: 'modal toast',
          position: 'bottom',
          duration: 3000
        }));
    }

    render() {
        return (
         <ModalContainer onShow={(e) => { this.toastInstance = e; }}>
              <Text>Hi</Text>
         </ModalContainer>
        )
    }
}

Jhony-Reyes avatar Jul 10 '19 20:07 Jhony-Reyes