Search code examples
react-nativereparenting

How to reparent a component in ReactNative?


In the below code, I expected the webView content to not change when the clicks are increased, but every time it loads, a new timestamp is displayed.

const webView = (
  <WebView
    source={{
      uri:
        'data:text/html,<html><script>document.write("<h1 style=\\"font-size:64px\\">"+Date.now()+"<h1>");</script>',
    }}
  />
);


export default class App extends React.Component {
  state = {
    clicks: 0,
  };

  onClick = () => {
    this.setState({ clicks: this.state.clicks + 1 });
  };

  render() {
    return (
      <View>
        <Text onPress={this.onClick}>
          Click Me: {this.state.clicks}
        </Text>

        {this.state.clicks % 2 === 0 ? webView : null}
        {this.state.clicks % 2 === 1 ? webView : null}
      </View>
    );
  }
}

Link to expo snack to check it on a device.

So far, I've read about reparenting in React on issues here, implementing using Portals, and also saw an issue on supporting reparenting in react native with no resolution.

So, how to reuse a component instance in across multiple screens with out creating a new instance of it in every screen?

Was hoping reparenting would be the answer, but can't find any implementations, so if reparenting is the answer to this, how to implement it myself?


Solution

  • You definitely need to reparenting the view. I searched some libs that work as React Portals does.

    We have two projects available:

    I tested the second package (rn-native-portals) and this magically worked on Android:

    How to install

    1. npm install mfrachet/rn-native-portals

    2. react-native link (unfortunately we can't auto-link this yet, but we can submit PR)

    Implementation

    Your target element needs to be inside <PortalOrigin>

    import React from "react";
    import { View, Text, TouchableOpacity } from "react-native";
    import { PortalOrigin } from 'rn-native-portals';
    
    class Target extends React.Component {
      state = {
        moveView: false,
      }
    
      render() {
        return (
          <>
            <TouchableOpacity
              style={{ flex: 1 }}
              onPress={() => this.setState({ moveView: !this.state.moveView })}
            >
              <Text>Press Here</Text>
            </TouchableOpacity>
    
            <PortalOrigin destination={this.state.moveView ? 'destinationPortal' : null}>
              <View>
                <Text>This text will appear on destination magically...</Text>
              </View>
            </PortalOrigin>
          </>
        );
      }
    }
    
    export default Target;
    

    On destination use this (don't forget set the same unique portal's name)

    import React from "react";
    import { PortalDestination } from "rn-native-portals";
    
    class Destination extends React.Component {
      render() {
        return (
          <PortalDestination name="destinationPortal" />
        );
      }
    }
    
    export default Destination;
    

    This project is amazing, but definitely need our community help to create a better documentation.

    I have one project that need to use this feature, reparenting a video to the outside of screen. I'm seriously considering PR auto-link support to avoid compiling warnings.

    More useful info about: