Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 992
Prasanth Avatar asked May 11 '20 10:05

Prasanth


2 Answers

The problem here is that on every state change your component will re-render webView object and will show the current date. I suggest that you change webView to a component and add a static key when you call WebViewComp to prevent unmount/mount on every state change.

const WebViewComp = () => ( //Change declaration to function component instead of constant object
  <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 style={styles.container}>
        <Text style={styles.paragraph} onPress={this.onClick}>
          Click Me: {this.state.clicks}
        </Text>

        {this.state.clicks % 2 === 0 ? <WebViewComp key="child" /> : null}
        {this.state.clicks % 2 === 1 ? <WebViewComp key="child" /> : null}
      </View>
    );
  }
}
like image 141
Mahdi N Avatar answered Sep 22 '22 16:09

Mahdi N


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

We have two projects available:

  • https://github.com/zenyr/react-native-portal

  • https://github.com/mfrachet/rn-native-portals

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:

  • The project concept: https://github.com/mfrachet/rn-native-portals/blob/master/docs/CONCEPT.md

  • Why the project was created (long history): https://tech.bedrockstreaming.com/6play/how-a-fullscreen-video-mode-ended-up-implementing-react-native-portals/

like image 31
Maycon Mesquita Avatar answered Sep 21 '22 16:09

Maycon Mesquita