Code Monkey home page Code Monkey logo

react-native-portalize's Introduction

Portalize

npm version

The simplest way to render anything on top of the rest.

This component is extracted from react-native-paper and has been simplified for the purpose of react-native-modalize.

Installation

yarn add react-native-portalize

Usage

import React from 'react';
import { View, Text } from 'react-native';
import { Host, Portal } from 'react-native-portalize';

export const MyApp = () => (
  <Host>
    <View>
      <Text>Some copy here and there...</Text>

      <Portal>
        <Text>A portal on top of the rest</Text>
      </Portal>
    </View>
  </Host>
);

Example with react-native-modalize and react-navigation

import React from 'react';
import { View, TouchableOpacity, Text } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Modalize } from 'react-native-modalize';
import { Host, Portal } from 'react-native-portalize';

const Tab = createBottomTabNavigator();

const ExamplesScreen = () => {
  const modalRef = useRef<Modalize>(null);

  const onOpen = () => {
    modalRef.current?.open();
  };

  return (
    <>
      <TouchableOpacity onPress={onOpen}>
        <Text>Open the modal</Text>
      </TouchableOpacity>

      <Portal>
        <Modalize ref={modalRef}>...your content</Modalize>
      </Portal>
    </>
  );
};

const SettingsScreen = () => (
  <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
    <Text>Settings screen</Text>
  </View>
);

export const App = () => (
  <NavigationContainer>
    <Host>
      <Tab.Navigator>
        <Tab.Screen name="Examples" component={ExamplesScreen} />
        <Tab.Screen name="Settings" component={SettingsScreen} />
      </Tab.Navigator>
    </Host>
  </NavigationContainer>
);

Props

Host

  • children

A React node that will be most likely wrapping your whole app.

Type Required
node Yes
  • style

Optional props to define the style of the Host component.

Type Required
style No

Portal

  • children

The React node you want to display on top of the rest.

Type Required
node Yes

react-native-portalize's People

Contributors

benjaminreid avatar harveyappleton avatar jeremybarbet avatar mavarazy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

react-native-portalize's Issues

How to handle on Refresh on the modal if it has a flatlist

@jeremybarbet How do i handle the on refresh of a flat-list using modalize?
currently when i try to swipe down on the flatlist the modal goes down as if to close.Is this the default behaviour
Here is my implementation

<Portal>
     <Modalize
       modalHeight={600}
       ref={innerRef}
       HeaderComponent={
         <>
           <Text style={styles.title}>Confirm Mpesa Payments</Text>
           <Text style={styles.subtitle}>Latest Payments</Text>
         </>
       }
       flatListProps={{
         horizontal: false,
         keyExtractor: keyExtractor,
         data: payments,
         renderItem: renderItem,
         onEndReachedThreshold: 0.9,
         initialNumToRender: 10,
         onEndReached: LoadMoreProducts,
         refreshing: refresh,
         onRefresh: Refresh,
         contentContainerStyle: {
           flexGrow: 1,
           paddingBottom: 100,
         },
         ListFooterComponent: loading && (
           <View style={(alignItems = "center")}>
             <ActivityIndicator size="large" color="#f2f2f2" />
           </View>
         ),
         showsVerticalScrollIndicator: false,
       }}
     />
   </Portal>

Couldn't find a navigation object

Hello,
I am using this library and it works pretty well, however I am experiencing a strange issue :

The app's root component is configured in this way

Screenshot 2021-05-21 at 15 09 37

So the <Host /> is inside a navigation context.

The Portal works and it's content is shown as desired, but if inside the components that I put inside the portal I use react-navigation's useNavigation, I get the following error:

Screenshot 2021-05-21 at 15 21 59

So it seems that the navigation context get loosed somewhere.

Am I doing something wrong o is it some sort of bug?

I am using the 1.0.7 version and These are my system's informations

System:
OS: macOS 11.3.1
CPU: (16) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
Memory: 3.44 GB / 16.00 GB
Shell: 5.8 - /bin/zsh
Binaries:
Node: 12.18.3 - ~/.nvm/versions/node/v12.18.3/bin/node
Yarn: 1.22.5 - ~/.yarn/bin/yarn
npm: 6.14.6 - ~/.nvm/versions/node/v12.18.3/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
Managers:
CocoaPods: 1.10.1 - /usr/local/bin/pod
SDKs:
iOS SDK:
Platforms: iOS 14.5, DriverKit 20.4, macOS 11.3, tvOS 14.5, watchOS 7.4
Android SDK:
API Levels: 23, 25, 26, 27, 28, 29, 30
Build Tools: 23.0.1, 26.0.3, 28.0.3, 29.0.2, 29.0.3, 30.0.2, 30.0.3
System Images: android-30 | Google APIs Intel x86 Atom
Android NDK: Not Found
IDEs:
Android Studio: 4.0 AI-193.6911.18.40.6626763
Xcode: 12.5/12E262 - /usr/bin/xcodebuild
Languages:
Java: 1.8.0_242-release - /usr/bin/javac
Python: 2.7.16 - /usr/bin/python
npmPackages:
@react-native-community/cli: Not Found
react: 16.13.1 => 16.13.1
react-native: 0.63.4 => 0.63.4
react-native-macos: Not Found
npmGlobalPackages:
react-native: Not Found

Why index in the Portal key?

Hi, we were having some problems in our app with components being unmounted in an unpredictable way. We traced it back to this line below.

Let's say a portal was unmounted, doesn't having the index in the key ensures that those Portals in the list after the unmounted one rerenders because of the new key, but the ones in the beginning of the list will not? (since their key will be the same due to being at the same index as before)

react-native-portalize-jibberishkey000-0
react-native-portalize-jibberishkey111-1
react-native-portalize-jibberishkey222-2

unmount react-native-portalize-jibberishkey111-1

react-native-portalize-jibberishkey000-0 <- same key
react-native-portalize-jibberishkey222-1 <- new key

key={`react-native-portalize-${key}-${index}`}

modal not show above navigation?

Hi, can u help:

here is my app:

<NavigationContainer ref={RootNavigation.navigationRef}
                        onReady={() => {
                          SplashScreen.hide()
                          RootNavigation.isReadyRef.current = true
                        }}>
                        <Host>
                        <Stack.Navigator>
                        ....
                        </Stack.Navigator>
                        </Host>
                      </NavigationContainer>

here is my sheet:

<Portal>
        <Modalize
           
        </Modalize>
      </Portal>

react-native-modalize

I have 2 (react-navigation 5) screens which wrapped by your component

  • List
  • Details

and one component which is

<Portal >
        <Modalize
          modalStyle={[styles.modal]}
          adjustToContentHeight={true}
          FooterComponent={
            <Button
              disabled={this.reject_message.length === 0}
              text="SEND"
              onPress={this.onPress}
              style={styles.btn}
            />
          }
          HeaderComponent={<Text style={styles.menuTitle}>{title}</Text>}
          ref={this.modal}>
          <Input
            name="reject_message"
            onChangeText={this.onChange}
            label={t('ENTER_REASON')}
            type="simple"
            multiline
            numberOfLines={3}
            value={this.reject_message}
          />
        </Modalize>
      </Portal>

The is placed on each screens (List,Details).

  1. open modal on List screen
  2. navigate to Details screen
  3. open modal on Details screen
  4. go back to List screen and try to open modal again, and it is not showing

ezgif com-video-to-gif (2)

Cannot swipe to close Modalize inside Portalize

I've made several Android applications with modalize and portalize, but this time I got a problem swipe to close modalize can't work,
but on IOS devices it can run fine, or if I don't use portalize then modalize runs fine

dependencies

{
    "react": "17.0.2",
    "react-native": "0.67.4",
    "react-native-gesture-handler": "^2.1.1",
    "react-native-modalize": "^2.0.13",
    "react-native-portalize": "^1.0.7"
}

App.tsx

 import {NavigationContainer} from '@react-navigation/native';
 import React from 'react';
 import {navigationRef} from './helper';
 import Router from './router';
 import { Host } from 'react-native-portalize';
 const App = () => {
   return (
      <Host>
         <NavigationContainer ref={navigationRef}>
            <Router />
         </NavigationContainer>
      </Host>
   );
 };
 export default App;

my program snippet:

import React, { useRef, useState } from 'react';
import { SafeAreaView, StyleSheet, TouchableOpacity } from 'react-native';
import { Modalize } from 'react-native-modalize';
import { Portal } from 'react-native-portalize';
import { FlatGrid } from 'react-native-super-grid';
import { FormControllerProfile } from '../../components';

export default function Example() {
  const modalizeRef = useRef(null);
  const [items, setItems] = useState([]);
  const [formData, setFormData] = useState({});

  const renderBoxItem = item => {
    return (
      <TouchableOpacity
        onPress={() => modalizeRef.current?.open()}>
      </TouchableOpacity>
    );
  };
  return (
    <>
      <FlatGrid
        itemDimension={130}
        data={items}
        style={styles.gridView}
        spacing={10}
        renderItem={({item}) => renderBoxItem(item)}
      />
      <SafeAreaView style={{flex: 1}}>
        <Portal>
          <Modalize ref={modalizeRef}>
            <FormControllerProfile
              onSubmit={val => modalizeRef.current?.close()}
              defaultValue={formData}
            />
          </Modalize>
        </Portal>
      </SafeAreaView>
    </>
  );
}

My Ios Emulator
ezgif-4-7571a5d7b6

My Android Emulator
ezgif-1-107bdcdd8a

Context doesn't work inside Portal

Hello.

I create codesandbox example about the bug. Check it first.

Expected behavior:
Component inside <Portal> has access to parent Context (inside <Host> not outside context).

Current behavior:
Component can has access only if <Host> wrapped by Context but always Context exist inside nested components only

Remounting other portals closes other portals

Hello everyone. I am using this library in my app right now and am experiencing what looks like a bug. Tell me your thoughts....

The use case in my app is the following. I have multiple modals on a single screen. Each modal is using Modalize modal in a separate portal. There is one component that remounts when date changes which will remount the Modal and in return the portal as well. The issue is if I have a modal open in a portal and another portal remounts then the current portal closes and the modal does not fire onClose so it cannot reopen.

This only happens if the portal remounting is before the open portal in the DOM. I think portals should be able to remount without affecting other portals.

I have included a demo. In the demo if you click "Open a" modal "A" opens. 4 seconds later I remount modal B by changing the key. Remounting modal B closes modal A. Create a file with following code and import App into the index of a set up react-native project to test! :)

Versions
react-native-portalize: 1.0.4
react: 16.8.6
react-native: 0.60.4

import React, { useEffect, useRef, useState } from 'react';
import { Portal, Host } from 'react-native-portalize';
import { Modalize } from 'react-native-modalize';
import {
  Text,
  View,
  TouchableOpacity,
  StyleSheet,
} from 'react-native';

const App = () => {
  return (
    <View style={{ flex: 1 }}>
      <Host>
        <Test />
      </Host>
    </View>
  );
};

const Button = ({ onPress, title }) => (
  <TouchableOpacity onPress={onPress} style={styles.button}>
    <View
      style={{
        alignItems: 'center',
        justifyContent: 'center',
      }}>
      <Text>{title}</Text>
    </View>
  </TouchableOpacity>
);

const Test = () => {
  const [modalAOpen, setModalAOpen] = useState(false);
  const [modalBOpen, setModalBOpen] = useState(false);
  const [bKey, changeBKey] = useState(Math.random());

  return (
    <View style={{ flex: 1, justifyContent: 'center' }}>
      <Button
        onPress={() => {
          setModalAOpen(true);
          setTimeout(() => changeBKey(Math.random()), 4000);
        }}
        title={'Open a'}
      />
      <Button onPress={() => setModalBOpen(true)} title={'Open b'} />

      <Modal
        title={'b'}
        key={bKey}
        visible={modalBOpen}
        onClosed={() => setModalBOpen(false)}>
        <View style={{ flex: 1, backgroundColor: 'red' }} />
      </Modal>

      <Modal
        visible={modalAOpen}
        onClosed={() => setModalAOpen(false)}
        title={'a'}>
        <View style={{ flex: 1, backgroundColor: 'blue' }} />
      </Modal>
    </View>
  );
};

const Modal = props => {
  const hasMounted = useRef();
  const ref = useRef();

  useEffect(() => {
    console.log('mount', props.title);
    return () => {
      console.log('unmount', props.title);
    };
  }, []);

  useEffect(() => {
    if (hasMounted.current) {
      if (props.visible) {
        ref.current?.open();
      } else {
        ref.current?.close();
      }
    } else {
      hasMounted.current = true;
    }
  }, [props.visible]);

  return (
    <Portal>
      <Modalize ref={ref} onClosed={props.onClosed} {...props}>
        {props.children}
      </Modalize>
    </Portal>
  );
};

const styles = StyleSheet.create({
  button: {
    backgroundColor: "#e6e6e6",
    paddingVertical: 12,
    minWidth: 200,
    justifyContent: 'center',
    alignItems: 'center',
    borderRadius: 15,
  },
});

export default App;

Mounting a second portal breaks PanGestureHandler in first portal

Description

I just found out that mounting a second portal will break PanGestureHandlers in other portals. The swipe gestures just don't get recognized after the second portal mounts. I need to put PanGestureHandler inside Portal because swipe gestures wont work inside Portal if the Handler is on the parent. Anyone have an idea how to fix this or work around it?

Related issue about mounting portals breaking things. #12

Thanks!

Demo

import React, { useState } from 'react';
import { View, TouchableOpacity } from 'react-native';
import { PanGestureHandler } from 'react-native-gesture-handler';
import Animated, { event } from 'react-native-reanimated';
import { Portal } from 'react-native-portalize';
import { DEVICE_HEIGHT, DEVICE_WIDTH } from '../styles';

export const TestScreen = () => {
  const [mountPortal, setMountPortal] = useState(false);

  const x = new Animated.Value(0);
  const y = new Animated.Value(0);

  const gestureEvent = event([
    {
      nativeEvent: {
        translationX: x,
        translationY: y,
      },
    },
  ]);

  return (
    <View
      style={{
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
        padding: 50,
      }}>
      <TouchableOpacity
        onPress={() => setMountPortal(true)}
        style={{ height: 50, width: 100, backgroundColor: '#000' }}
      />
      {mountPortal && <Portal />}
      <Portal>
        <PanGestureHandler onGestureEvent={gestureEvent}>
          <Animated.View
            style={{
              transform: [{ translateY: y }, { translateX: x }],
              width: 50,
              height: 50,
              borderRadius: 25,
              backgroundColor: '#ccff00',
              position: 'absolute',
              left: DEVICE_WIDTH / 2 - 25,
              top: DEVICE_HEIGHT / 2 - 25,
            }}
          />
        </PanGestureHandler>
      </Portal>
    </View>
  );
};

Can't click the buttons on the screen after the modal is closed

So I am using Portalize and Modalize. Wrap one of my Stack navigations with Host, and mount the modal in one of the screens of that stack, and open and close that model in the same screen. When I close the modal, for like half a second after, I cannot click on any buttons in that screen, as if the modal is not properly being closed/unmounted even though it is not there anymore. To click any buttons after the modal is closed, I either have to double click, or wait like half a sec and then click any button on that screen, which is very odd. This only happens if I open the modal and close it. When I remove the modal code from the screen, or not open it at all, the buttons are working fine. So there is no issue with the buttons.

This is my modal:

    <Portal>
      <Modalize
        closeAnimationConfig={{
          spring: {
            tension: 0,
          },
          timing: {
            duration: 500,
          },
        }}
        ref={modalRef}
        withHandle={false}
        adjustToContentHeight={!height}
        panGestureEnabled={!disableSwipeDown}
        modalHeight={height || null}
        scrollViewProps={{
          scrollEnabled: false,
        }}
      >
        {children}
      </Modalize>
    </Portal>

And this is how I use it in the screen:

  const modalRef = useRef(null);

 <Modal modalRef={modalRef}>
       //Children to render in the modal view
        <FooterContent modalRef={modalRef} />
 </Modal>

Picker not working

In the portal a react native picker does not update its value.

"react-native-portalize": "^1.0.1",

when i use the official callstack-paper component it works.

ezgif-3-e2f6f5d2d4e6 (1)

Ref's are null when remouting

Currently, the modalized modal is losing its reference when being unmounted and remounted again by its parent - resulting in a null value.

Here's an example of what that looks like:

const Parent = () => {
// IF THIS 'someCondition' DYNAMICALLY CHANGES, THE REF IS LOST WITH THE PORTAL, not using portals it works correctly (apart from styling issues)
  if (someCondition) {
    return <SomethingElse />
  }

  return (
    ...
    ...
    <Modal open={someState} onClose={() => setSomeState(false)}>Some content</Modal>
  )
}

const Modal = ({ open, onClose: handleClose, children }: IProps) => {
  // THIS REF IS LOST AFTER 'someCondition' IN PARENT COMPONENT INVERTS TWICE
  const modalRef = useRef<Modalize>(null)

  useEffect(() => {
    if (open) modalRef.current?.open()
  }, [open, modalRef])

  return (
    <Portal>
      <Modalize ref={modalRef} snapPoint={400} adjustToContentHeight onClose={handleClose}>
        <View style={styles.content}>{children}</View>
      </Modalize>
    </Portal>
  )
}

Do you have any ideas on how this can be solved?

Making other portals above than the others

If I call <Portal> on multiple components, there's a tendency for some components to be above than the other components, but what if I want to be able to order them, like <Portal priority={1}> the higher the number provided the higher it is on the stack, so that priority={10} is on top of priority={9}.

Ref’s becoming null

Hello 👋

I ran into an issue today using react-navigation, using a stack navigator and having different <Portal>s in each tab.

Each <Portal> would have a ref to a Modalize component as it’s child component and due to some behaviour of switching tabs, putting the app into the background and back to the foreground, the ref’s to those Modalize components would be lost (resulting them being set back to null).

I’ve checked out react-native-portalize locally and replaced the mount function in Host.tsx with...

  const mount = (children: React.ReactNode): number => {
    const key = nextKey++;

    if (managerRef.current) {
      // @ts-ignore
      const mountKey = children.key && children ? children.key : key;
      managerRef.current.mount(mountKey, children);
    } else {
      // @ts-ignore
      const mountKey = children.key && children ? children.key : key;
      queue.push({ type: 'mount', key: mountKey, children });
    }

    return key;
  };

This is essentially allowing the component’s rendered inside of <Portal> to mount with their own keys.

It seems to be working, besides the TS issue which I haven’t looked into yet and just ignored for now (accessing children.key) and not sure what other parts of the codebase this would impact.

@jeremybarbet What do you think of this? Basically the numerical key that’s generated in some scenario seems to be clashing. I ended up logging the the portals array in Manage.tsx and it ended up with duplicate keys [{ key: 0, ... }, { key: 0 ... }]`, thus trying to specify unique keys from the components themselves.

Again, awesome work with these components, any help would be massively appreciated. Happy to draft up a PR if you’d like.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.