How to reparent a component in ReactNative? - react-native

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?

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>
);
}
}

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
npm install mfrachet/rn-native-portals
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/

Haven't tried the accepted answer's projects but, for React Native, #gorhom/portal works like a charm retaining context like a champ!

Related

Cannot display firestore info in react native view

I am trying ti display basic profile data from firestore on a profile page, but the information will not show up in the view. My code is below: I create a constructor with the state ethnicity
var db = firebase.firestore();
export default class Dashboard extends Component {
constructor() {
super();
this.state = {
displayName: firebase.auth().currentUser.displayName,
uid: firebase.auth().currentUser.uid,
ethnicity: ''
}
}
And I then update the state with componentDidMount() as shown below and then try to display it in view
componentDidMount() {
db.collection("profiles").doc(this.state.uid).get()
.then(doc => {
this.state.ethnicity= doc.data().ethnicity.toString();
console.log(this.state.ethnicity)
})
.catch(function(error) {
console.log("Error getting document:", error);
})
}
render() {
return (
<View style={styles.container}>
<Text style = {styles.textStyle}>
Mixee!
</Text>
<Text style = {styles.textStyle}>
Hello, {this.state.displayName}
</Text>
<Text style = {styles.textStyle}>
Profile:
</Text>
<Text style = {styles.textStyle}>
Ethnicity: {this.state.ethnicity}
</Text>
<Button
color="#3740FE"
title="Logout"
onPress={() => this.signOut()}
/>
</View>
);
}
The console.log(this.state.ethnicity) correctly prints out the ethnicity so I do not know why it isn't showing up. Any help appreciated thanks!
It looks like you are modifying state directly, with this.state.ethnicity = doc.data().ethnicity.toString(); This might not cause a re-render, so your component does not update with the new state. Instead, try:
this.setState({
ethnicity: doc.data().ethnicity.toString()
});
Calling setState will let React Native know that the state has changed, and that it should call render again, as explained here.

View style is not working in React Native

Im new in react native,
before in my code using const Main = () => { ..}
but when i change to export default class Main extends React.Component { ..} , problems arise like style in View not working but the error ',' expected in style
Screnshoot :
im using new version of react native
You are missing the render() method where the return() method should be.
Ex:
export default class LotsOfStyles extends Component {
render() {
return (
<View>
<Text style={styles.red}>just red</Text>
<Text style={styles.bigBlue}>just bigBlue</Text>
<Text style={[styles.bigBlue, styles.red]}>bigBlue, then red</Text>
<Text style={[styles.red, styles.bigBlue]}>red, then bigBlue</Text>
</View>
);
}
}
from react native docs

Null is not an object while creating navigation between screens in expo project

I need to open new screen after clicking on the button. For it I made the following steps:
1) Installed this library
2) Created a new screen and added it to the folder with other screens (DetailInfoScreen is a new screen, which should be opened and HomeScreen is a screen, where a button, after clicking on which new screen should be opened):
3) Added the following lines of code:
import { Navigation } from 'react-native-navigation';
import DetailInfoScreen from './DetailInfoScreen';
class HomeScreen extends Component {
constructor(props) {
super(props);
this.onPressSearch = this.onPressSearch.bind(this);
Navigation.registerComponent('DetailInfoScreen', () => DetailInfoScreen);
}
goToScreen = (screenName) => {
Navigation.push(this.props.componentId, {
component: {
name: screenName
}
});
}
render() {
const { list, text } = this.props;
return (
<View style={styles.container}>
<View style={styles.searchContainer}>
<TouchableOpacity
onPress={this.goToScreen('DetailInfoScreen')}
>
<View>
<Text>Search</Text>
</View>
</TouchableOpacity>
</View>
);
}
But when I run the project I have the following error:
And one more moment which disturbs me is that autocorrection in vscode doesn't see my new screen while importing:
Maybe it doesn't play any role, but still. So, what's the reason of the problem and how can I solve it?
You can simply navigate to another screen by using this:
<TouchableOpacity
onPress={() => this.props.navigation.navigate('DetailInfoScreen')>
<Text>Search</Text>
</TouchableOpacity>
I would personally advice for you to use react-navigation instead of react-native-navigation, you can read more on this link

React Native - Push one Screen To Another have issue

I used the 'StackNavigator', I have issue to push when navigation code write in button event. but its working fine if we directly code onPress event,
Import file.
import { StackNavigator } from "react-navigation";
import SignUpScreen from "./SignUp";
Push another is working :
render() {
console.disableYellowBox = true;
const { navigate } = this.props.navigation;
return (
<View style={styles.viewStyle}>
<TouchableHighlight style = {styles.buttonStart}
onPress={() => navigate("SignUpCompany")}>
<Image
source={require('./Images/hire.png')}
/>
</TouchableHighlight>
</View>
);
}
Push another via function is not working :
pushcode() {
console.log('call');
this.props.navigation.navigate('SignUp');
}
render() {
return (
<TouchableHighlight style = {styles.buttonStart}
onPress={this.pushcode}>
<Image
source={require('./Images/hire.png')}
/>
</TouchableHighlight>
);}
ERROR ON CLICK BUTTON :
Thanks. Please help me.
Look like you are using the push extra in this line
this.props.navigation.navigate.push('SignUp');
Try this will work for you
this.props.navigation.navigate('SignUp');
May be this can help you
I think you miss the function bind within constructor --- that's why you got undefined is not an object (evaluating 'this.props.navigation'). because this is undefined inside pushcode function scope.
add below into your constructor:
constructor(props) {
super(props);
this.pushcode = this.pushcode.bind(this);
...
}

How to bind(this) when passing a function as a prop in react native

I am working on a simple calculator app to learn react native. I have a button component that displays the number buttons. I need to pass it a function so that when the button is touched the state of the parent is updated to the new number.
Without the bind(this) it says this.setState is undefined. When I add bind(this) in the constructor all I get is a blank page in my app. My code is listed below.
constructor:
constructor() {
super();
this.state = {
total: 0,
display: 0
};
this._displayUpdate = this._displayUpdate.bind(this);
}
One row of calculator buttons:
<View style={styles.buttonRow}>
<NumButton numText={7} update={this._displayUpdate} />
<NumButton numText={8} update={this._displayUpdate} />
<NumButton numText={9} update={this._displayUpdate} />
<FuncButton funcText={'*'} />
</View>
NumButton Component:
export default class NumButton extends Component {
render() {
return (
<TouchableHighlight style={styles.buttonArea} onPress={this.props.update(this.props.numText)}>
<Text style={styles.buttonText}>{this.props.numText}</Text>
</TouchableHighlight>
)
}
}
Your parent bind is right. The problem is with the onPress of the TouchableHighlight in the NumButton component. You can't add code to execute to it, You should pass a function to it. You can use ES6 arrow function:
export default class NumButton extends Component {
render() {
return (
<TouchableHighlight style={styles.buttonArea}
onPress={()=>{ this.props.update(this.props.numText) }}
>
<Text style={styles.buttonText}>{this.props.numText}</Text>
</TouchableHighlight>
)
}
}