React Native Display Rendering - javascript

I updated the code to reflect Denis' solution. However, react is now no longer responding to commands sent from the node server
export default class Display extends Component {
constructor(props){
super(props);
this.state = {
ren: "",
action: "background",
media: "white",
backgroundColor: 'white',
uri: "https://www.tomswallpapers.com/pic/201503/720x1280/tomswallpapers.com-17932.jpg"
};
}
componentDidMount(){
this.startSocketIO();
}
componentWillUnmount(){
socket.off(Socket.EVENT_CONNECT);
}
startSocketIO(){
socket.on('function_received', func_var => {
let command = func_var.split(" ");
this.setState({action: command[0]});
this.setState({media: command[1]});
console.log(this.state.action);
console.log(this.state.media);
switch(this.state.action){
case 'image':
this.setState({ uri: this.state.media});
console.log(this.state.uri);
case 'background':
this.setState({ backgroundColor: this.state.media});
console.log(this.state.backgroundColor);
default:
console.log(this.state.backgroundColor);
// return (<View style={{backgroundColor: this.state.backgroundColor, flex: 1}} />);
}
});
}
render(){
return (
null
);
}
}
I'm currently working on a basic react native app that displays images received in uri format from a node server and changes the background color. Separately, both of my implementations work. (See the BackGround and ImageServer components) I'm now attempting to combine the functionality of both components into one component named display. So far my current code looks like it should work without issue however after sending a command to the device via socket.io it appears that the render doesn't go any further since my console logs stop populating after a test. I'm not sure if there is an issue with the setup of the switch statement or if I'm somehow causing a race condition. Any insight would be greatly appreciated!
import React, { Component } from 'react';
import {Image, Text, StyleSheet, Button, View, Dimensions, Vibration} from 'react-native';
const io = require('socket.io-client');
//change this to your public ip "google what is my ip address"
//This will be modified in the future to grab the ip address being used by
//the node server
let server = 'redacted';
let socket = io(server, {
transports: ['websocket']
});
export default class Display extends Component {
constructor(props){
super(props);
this.state = {
action: "background",
media: "white",
backgroundColor: 'white',
uri: "https://www.tomswallpapers.com/pic/201503/720x1280/tomswallpapers.com-17932.jpg"
};
}
render(){
socket.on('function_received', func_var => {
var command = func_var.split(" ");
this.setState({action: command[0]});
this.setState({media: command[1]});
});
console.log(this.state.action);
console.log(this.state.media);
switch(this.state.action){
case 'image':
this.setState({ uri: this.state.media});
return (
<Image
source={{uri: this.state.uri}}
style={styles.fullscreen} />
);
case 'background':
this.setState({ backgroundColor: this.state.media});
return (<View style={{backgroundColor: this.state.backgroundColor, flex: 1}} />);
default:
return (<View style={{backgroundColor: this.state.backgroundColor, flex: 1}} />);
}
}
}
export class BackGround extends Component {
constructor(props){
super(props);
this.state = {
backgroundColor: 'black'
};
}
render(){
socket.on('function_received', func_var => {
this.setState({ backgroundColor: func_var});
});
return (
<View style={{backgroundColor: this.state.backgroundColor, flex: 1}} />
);
}
}
export class ImageServer extends Component {
constructor(props){
super(props);
this.state = {
uri: "https://www.tomswallpapers.com/pic/201503/720x1280/tomswallpapers.com-17932.jpg"
};
}
render() {
socket.on('function_received', func_var => {
//Vibration.vibrate([0, 500, 200, 500,200,500,200,500,200,500,200,500,200], false);
this.setState({ uri: func_var});
});
return(
<Image
source={{uri: this.state.uri}}
style={styles.fullscreen}
/>
);
}
}

You code should like this
componentDidMount() {
this.startSocketIO();
}
startSocketIO() {
socket.on('some_method', (response) => {
this.setState({ key: response.some_value })
});
}
componentWillUnmount() {
// unsubscribe socket.io here !important
}
render() {
return (
// JSX
);
}

Related

React native - is there a way to use an object from another page?

I want to know how can I use the "userPrincipalName" on other pages.
what do I need to do to make it work?
in my example, I try to use the "userPrincipalName" object on the MainScreenpage but I don't understand how to do it.
this my example of the Modal page which it has the object "userPrincipalName":
import React,
{ Component } from 'react';
import {
Text,
TouchableOpacity,
StyleSheet,
View,
} from 'react-native';
import Modal from 'react-native-modal';
import PlacesNavigator from '../navigation/PlacesNavigator';
import { LinearGradient } from 'expo-linear-gradient';
import { AzureInstance, AzureLoginView } from 'react-native-azure-ad-2';
const credentials = {
client_id: 'ea00ca9e-8c37-4520-8d80-2c2bb9239bf8',
scope: 'User.Read',
};
export default class Example extends Component {
constructor(props) {
super(props);
this.state = {
visibleModal: 3,
azureLoginObject: {},
loginSuccess: false
};
this.azureInstance = new AzureInstance(credentials);
this._onLoginSuccess = this._onLoginSuccess.bind(this);
}
_renderButton = () => (
<TouchableOpacity
onPress={() => this.setState({ visibleModal: false })}>
<LinearGradient
colors={['#4c669f', '#3b5998', '#192f6a']}
style={{
height: 80,
width: 180,
borderRadius: 10,
backgroundColor: "#2196F3",
justifyContent: 'center',
alignItems: 'center',
marginTop: 50,
}}>
<Text style={{ color: 'white', fontSize: 20, fontWeight: 'bold' }}>כניסה</Text>
</LinearGradient>
</TouchableOpacity>
);
_onLoginSuccess() {
this.azureInstance.getUserInfo().then(result => {
this.setState({
loginSuccess: true,
azureLoginObject: result,
});
console.log(result);
}).catch(err => {
console.log(err);
})
}
renderWelcomeMsg = (currentTime = new Date()) => {
const currentHour = currentTime.getHours()
const splitAfternoon = 12;
const splitEvening = 17;
if (currentHour >= splitAfternoon && currentHour <= splitEvening) {
return 'צהריים טובים,';
} else if (currentHour >= splitEvening) {
return 'ערב טוב,';
}
return 'בוקר טוב,';
}
render() {
if (!this.state.loginSuccess) {
return (
<AzureLoginView
azureInstance={this.azureInstance}
onSuccess={this._onLoginSuccess}
/>)
}
if (this.state.visibleModal === 3) {
const { givenName } = this.state.azureLoginObject;
const { userPrincipalName } = this.state.azureLoginObject;////THIS IS THE OBJECT I WANT
return (
<View style={styles.container}>
<Modal
isVisible={this.state.visibleModal === 3}
animationInTiming={1000}
animationOutTiming={1000}
backdropTransitionInTiming={4000}
backdropTransitionOutTiming={4000}
animationIn={'flipInY'}
>
<LinearGradient
colors={['#43D4FF', 'white']}
style={{ borderRadius: 10 }}>
<View style={styles.modalContent}>
<Text style={{
fontWeight: "bold",
fontSize: 35,
justifyContent: 'center',
alignItems: 'center',
}}>{this.renderWelcomeMsg()} {givenName}
</Text>
<View style={styles.buttonContainer}>
{this._renderButton()}
</View>
</View>
</LinearGradient>
</Modal>
</View>
);
}
return (
<PlacesNavigator />
);
}
}
And this is the MainScreen page that i want to use the object "userPrincipalName" in the Axios:
import React, { Component } from "react";
import {
View,
Text,
StyleSheet,
ActivityIndicator,
Platform,
FlatList,
TouchableOpacity,
TouchableHighlight,
WebView
} from "react-native";
import { HeaderButtons, Item } from "react-navigation-header-buttons";
import HeaderButton from "../components/HeaderButton";
import axios from "axios";
import moment from 'moment'
import storeService from '../components/storeService'
export default class MainScreen extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
userPrincipalName: null
};
}
getData = () => {
this.setState({ isLoading: true, data: [] })
axios.get("https://harigotphat1.mekorot.co.il/ConfirmPackaotWS/OrderApprove/OrderApp_Get_Orders_To_Approve/" + userPrincipalName.split('#')[0])
.then(res => {
this.setState({
isLoading: false,
data: res.data
});
console.log(res.data);
});
}
componentDidMount() {
this.props.navigation.setParams({ getData: this.getData });
// now we load the data we stored in the async storage
storeService.loadKey('userPrincipalName').then((res) => {
console.log("THIS IS THE userPrincipalName", res) //res will contain the value given the key, store this value in your state and use it any where in the component
})
this.getData()
// this.postData()
}
renderItems = (item, index) => {
const { merhavid, yaamID, ezorID, shemEzor } = item;
return (
<TouchableHighlight style={{
backgroundColor: '#ffff78'
}}>
<TouchableOpacity
style={{
paddingVertical: 15,
paddingHorizontal: 10,
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
borderWidth: 0.8,
borderColor: '#d5d7db',
backgroundColor: index % 2 == 0 ? '#d1f0da' : '#f2f5f3',
}}
onPress={() => this.props.navigation.navigate("Info")}>
<Text style={styles.name}>
{ezorID + "" + " |" + " " + merhavid + " " + yaamID + " " + shemEzor}
</Text>
</TouchableOpacity>
</TouchableHighlight>
);
}
render() {
if (this.state.isLoading) {
return (
<View style={{ flex: 0, paddingTop: 300 }}>
<Text style={{ alignSelf: "center", fontWeight: "bold", fontSize: 20 }}>טוען נתונים...</Text>
<ActivityIndicator size={'large'} color={'#08cbfc'} />
</View>
);
}
return (
<>
<View style={styles.container}>
<FlatList
data={this.state.data}
keyExtractor={(_, index) => String(index)}
renderItem={({ item, index }) => { return this.renderItems(item, index) }}
/>
</View>
<View style={styles.bottomMainContainer}>
<View style={styles.bottomView} >
<Text style={styles.bottomTextStyle}>סה"כ: {this.state.data.length} רשומות</Text>
</View>
</View>
</>
);
}
}
This is what React Context was designed for:
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
This also applies to updating data from nested components e.g.
const UserContext = React.createContext({
user: null,
setUser: () => {}
});
function UserContextProvider {
const [user, setUser] = useState(null);
return (
<UserContext.Provider value={{ user, setUser }}>
{props.children}
</UserContext.Provider>
)
}
function App() {
return (
<UserContextProvider>
<MainScreen />
</UserContextProvider>
);
}
class MainScreen extends Component {
static contextType = UserContext;
getData() {
// We should see the user details
console.log(this.context.user);
}
render() {
return (
<div>
<Example />
</div>
)
}
}
class Example extends Component {
static contextType = UserContext
_onLoginSuccess() {
this.azureInstance.getUserInfo().then(result => {
this.setState(...);
// set user in UserContext
this.context.setUser(result);
});
}
}
The best way would be use redux store which helps you create immutable data object which can be update only based on user action https://redux.js.org/basics/store/.
Another simple but not efficient way is to use the react native async storage, Where you store the object and later load it up in the componentDidMount() of your new component.
Another way is to pass these props as ScreenProps in your child component(this is only possible if the screens have a parent child relation)
solution - Sharing the object between components using asnc storage
import AsyncStorage from '#react-native-community/async-storage';
const storeService = {
async saveItem(key, value) {
try {
await AsyncStorage.setItem(key, value);
} catch (error) {
console.log('AsyncStorage Error: ' + error.message);
}
},
loadKey(key) {
return new Promise((resolve, reject) => {
AsyncStorage.getItem(key)
.then(res => {
resolve(res)
})
.catch(err => reject(err));
});
}
};
export default storeService;
Note that these stateless component has 2 methods, One is to save against a key and another is to load.
Now to save a value against a key use
import React,
{ Component } from 'react';
....
export default class Example extends Component {
constructor(props) {
super(props);
....
}
....
render() {
...
if (this.state.visibleModal === 3) {
const { givenName } = this.state.azureLoginObject;
const { userPrincipalName } = this.state.azureLoginObject;
//this is how we will store the value when this component Example loads
storeService.saveItem('userPrincipalName', userPrincipalName)
return (
....
);
}
return (
<PlacesNavigator />
);
}
}
And to load this item again use
import React, { Component } from "react";
...
export default class MainScreen extends Component {
constructor(props) {
super(props);
this.state = {
userPricipalName: null //initialize this state variable
data: []
};
}
getData = () => {
...
var userPrincipalName = this.state.userPrincipalName;
axios.get("https://harigotphat1.mekorot.co.il/ConfirmPackaotWS/OrderApprove/OrderApp_Get_Orders_To_Approve/"+userPrincipalName.split('#')[0])
....
}
componentDidMount() {
// now we load the data we stored in the async storage
storeService.loadKey('userPrincipalName').then((res) => {
this.setState({userPricipalName: res}) //store like this
})
this.getData()
}
...
render() {
....
return (
....
);
}
}
Note that in order to be able to save a whole object in async store you will first need to stringify the object which storing and json parse while reading it again.

React Native elements not rendering as expected

I am just getting started with React Native. I have created a new App using expo.io and am currently testing the components provided by https://react-native-elements.github.io
I am trying out input forms and buttons my code currently looks like this:
class EnterNewPasswordComp extends React.Component {
constructor(props) {
super(props);
this.state = {
pass : ''
}
}
render() {
return <View style={styles.container}>
<Text style={styles.label}>{pass_label}</Text>
<Input placeholder='Password' />
<Text style={styles.note}>{pass_desc}</Text>
<Button title="Submit" type="outline" />
</View>;
}
}
const styles = {
container : {
flex : 1,
justifyContent: 'center',
alignItems: 'center',
},
label : {
fontSize: 20,
},
note : {
fontSize: 12,
marginBottom: 20
},
input : {
color : style.txtcolor
},
btn : {
width : '50%'
}
};
the component (currently) only returning the EnterNewPasswordComp
class IndexComponent extends React.Component {
constructor(props) {
super(props);
this.passDao = Factory.getPasswordDao();
this.state = {
status: 'initializing'
}
}
async componentDidMount() {
try {
const pass = await this.passDao.getPassword();
logger.info(pass);
const setupNeeded = pass === null;
if( setupNeeded ) {
this.setState({
status : 'setup'
});
}
else {
this.setState({
status : 'ready'
})
}
} catch (e) {
logger.error("Error during db query", e);
this.setState({
status: 'corrupted'
});
}
}
render() {
let msg = "initializing";
switch (this.state.status) {
case 'initializing':
msg = 'initializing';
break;
case 'corrupted':
default:
msg = 'corrupted';
break;
case 'ready':
msg = 'ready';
break;
case 'setup':
return <EnterNewPasswordComp/>
}
return <Text>{msg}</Text>
}
}
and this component is used in my App.js like this:
export default class App extends Component {
render() {
return (
<View style={styles.container}>
<IndexComponent/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
}
});
This is how my output looks like on my Android Phone:
Note that:
The input field is not rendering at all
The button type outline is not picked up correctly
The button width is not picked up correctly
I played around with the styles, (like setting the color of the input-form, suspecting it might be the same as the background), but nothing changed at all. I am wondering if I am doing something fundamentally wrong to produce those issues.

ReactNative : Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state

This is my code, I can't seem to find the problem. I've researched the problem but wasn't able to find a solution so I'm turning to the StackOverflow gods for some help !
As you can see I want to load the fonts in my LoadingScreen and move to the next screen once it's done. if there's an easier way to proceed let me know.
import React from 'react';
import { StyleSheet, View, AsyncStorage, Alert, ActivityIndicator } from 'react-native';
import { LinearGradient } from 'expo-linear-gradient'
import * as Font from 'expo-font';
import * as firebase from "firebase";
export default class Loading extends React.Component {
constructor(props) {
super(props);
this.state = {
fontLoaded: false,
client: {
uid: ""
}
};
}
async componentWillMount() {
//Load fonts + Login to Firebase + capture user ID
let self = this;
await Font.loadAsync({
"Roboto-Regular": require("../assets/fonts/Roboto-Regular.ttf"),
"Courgette-Regular": require("../assets/fonts/Courgette-Regular.ttf"),
"Raleway-Regular": require("../assets/fonts/Raleway-Regular.ttf")
})
await firebase.auth().signInAnonymously().catch(function (error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
Alert.alert("Error code : " + errorCode, errorMessage)
});
//Register the UID
await this.setState({ uid: firebase.auth().currentUser.uid })
this.setState({
client: {
...this.state.client,
uid: firebase.auth().currentUser.uid
}
});
await this.setState({ fontLoaded: true })
}
render() {
if (this.state.fontLoaded) {
return (
this.props.navigation.navigate("Home", { client: this.state.client })
)
}
return (
<View style={styles.container}>
<LinearGradient
colors={["#5B86E5", "#36D1DC"]}
style={{ flex: 1 }}
>
<View style={{ justifyContent: "center", alignItems: "center", flex: 1 }}>
<ActivityIndicator size="large" color="#FFF" />
</View>
</LinearGradient>
</View >
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1
}
});
I think it is better to do load your Home Screen. It is not efficient to do an action like fetching data in a page and navigate to another page after action completion. I think it is better to fetch your data in componentDidMount lifecycle and when data received change your fontLoaded to true as below:
import React from 'react';
import { StyleSheet, View, AsyncStorage, Alert, ActivityIndicator } from 'react-native';
import { LinearGradient } from 'expo-linear-gradient'
import * as Font from 'expo-font';
import * as firebase from "firebase";
export default class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
fontLoaded: false,
client: {
uid: ""
}
};
}
async componentDidMount() {
//Load fonts + Login to Firebase + capture user ID
let self = this;
await Font.loadAsync({
"Roboto-Regular": require("../assets/fonts/Roboto-Regular.ttf"),
"Courgette-Regular": require("../assets/fonts/Courgette-Regular.ttf"),
"Raleway-Regular": require("../assets/fonts/Raleway-Regular.ttf")
})
await firebase.auth().signInAnonymously().catch(function (error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
Alert.alert("Error code : " + errorCode, errorMessage)
});
//Register the UID
await this.setState({ uid: firebase.auth().currentUser.uid })
this.setState({
client: {
...this.state.client,
uid: firebase.auth().currentUser.uid
}
});
await this.setState({ fontLoaded: true })
}
render() {
if (this.state.fontLoaded) {
return (
........... Any code that presents in your Home component
)
}
return (
<View style={styles.container}>
<LinearGradient
colors={["#5B86E5", "#36D1DC"]}
style={{ flex: 1 }}
>
<View style={{ justifyContent: "center", alignItems: "center", flex: 1 }}>
<ActivityIndicator size="large" color="#FFF" />
</View>
</LinearGradient>
</View >
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1
}
});
I hope it was helpful for you. If it was your desired solution please vote me up:)
You are mixing UI (render method) and functions (navigate inside the render). That method will execute multiple times, when React detects an update in the state or props.
If this is just a loading screen then remove the conditional from the render and just show a loading screen and navigate to the next screen from the componentDidMount method, which will only trigger once when the screen loads up.
That should remove the error. Basically, remove the setState({fontLoaded: true}) and just navigate to the next screen from there.

Why is socket.on() from a previous screen component executed?

I have two react components. the first Lobby uses react-native-navigation to push Gameroom to the stack. It passes props such as the socket object and other data to the Gameroom component
when the back button of the navigation bar is pressed inside Gameroom, a socket.io leave event is emitted, and I have verified it is heard by the server, so the socket passed through props works. the server then emits an event left back to the socket.io room (Gameroom component).
the left event listener, if placed inside Gameroom's componentDidMount() does not execute. However, if the same socket.io event listener is placed in Lobby component (previous screen) componentDidMount() the event is heard
I've tried adding the event listener to multiple componentDidMount functions, I also thought about using the Context API, but I'm not working with nested components. I'm passing the socket object in react-native-navigation's {passProps} from screen to screen
Lobby:
imports ...
const socket = io("http://192.xxx.xxx.xx:3000");
export default class Lobby extends React.Component {
static options(passProps) {
return {
topBar: {
background: {
color: "transparent"
},
drawBehind: true,
visible: true,
animate: true,
leftButtons: [
{
id: "leave",
icon: require("../assets/img/Chevron.png")
}
]
}
};
}
constructor(props) {
super(props);
this.state = {
username: "Initializing...",
queue: []
};
}
componentDidMount() {
Navigation.events().bindComponent(this);
socket.emit("lobbyEntry");
socket.on("lobbyEntry", entry => {
this.setState({ queue: entry.lobby, username: socket.id });
});
socket.on("userJoined", lobby => {
this.setState({ queue: lobby });
});
// socket.on("left", () => {
// alert("Opponent Left...Oh well");
// Navigation.pop(this.props.componentId);
// });
}
navigationButtonPressed({ buttonId }) {
switch (buttonId) {
case "leave":
socket.emit("leave");
Navigation.popToRoot(this.props.componentId);
break;
}
}
createMatch = () => {
if (this.state.username != "Initializing...") {
socket.emit("findMatch");
socket.on("alreadyCreated", () => {
alert("You already created a match!");
});
socket.on("listUsers", lobby => {
this.setState({ queue: lobby });
});
socket.on("matchFound", data => {
Navigation.push(this.props.componentId, {
component: {
name: "Gameroom",
passProps: {
room: data.id,
socket: socket,
firstMove: data.firstMove,
p1: data.p1,
p2: data.p2
}
}
});
});
} else {
alert("Wait for Username to be initialized...");
}
};
render() {
const bg = getBackground();
return (
<ImageBackground source={bg} style={{ height: "100%", width: "100%" }}>
<View style={styles.title_container}>
<Text style={styles.title_sm}>Matchmaking Lobby</Text>
</View>
<View style={styles.alt_text_container}>
<Text style={styles.alt_text_md}>Username:</Text>
<Text style={styles.alt_text_md}>{this.state.username}</Text>
</View>
<View
style={{
flexDirection: "column",
justifyContent: "center",
alignItems: "center"
}}
>
<XplatformButton onPress={this.createMatch} text={"Create a Match"} />
</View>
<View style={styles.alt_text_container}>
<Text style={styles.alt_text_sm}>Players actively searching...</Text>
<FlatList
style={styles.alt_text_container}
data={this.state.queue}
renderItem={({ item, index }) => (
<Text style={styles.alt_text_md} key={index}>
{item}
</Text>
)}
/>
</View>
</ImageBackground>
);
}
}
Gameroom:
import ...
export default class Gameroom extends React.Component {
static options(passProps) {
return {
topBar: {
title: {
fontFamily: "BungeeInline-Regular",
fontSize: styles.$navbarFont,
text: "Gameroom - " + passProps.room,
color: "#333"
},
background: {
color: "transparent"
},
drawBehind: true,
visible: true,
animate: true,
leftButtons: [
{
id: "leave",
icon: require("../assets/img/Chevron.png")
}
]
}
};
}
constructor(props) {
super(props);
Navigation.events().bindComponent(this);
}
navigationButtonPressed({ buttonId }) {
switch (buttonId) {
case "leave":
this.props.socket.emit("leave");
Navigation.pop(this.props.componentId);
break;
}
}
componentDidMount() {
// socket.on("left", () => {
// alert("Opponent Left...Oh well");
// Navigation.pop(this.props.componentId);
// });
}
render() {
const bg = getBackground();
return this.props.p2 != null ? (
<Gameboard
room={this.props.room}
you={
this.props.socket.id == this.props.p1.username
? this.props.p1.marker
: this.props.p2.marker
}
opponent={
this.props.socket.id != this.props.p1.username
? this.props.p2.marker
: this.props.p1.marker
}
move={this.props.firstMove}
socket={this.props.socket}
/>
) : (
<ImageBackground style={styles.container} source={bg}>
<View style={{ marginTop: 75 }}>
<Text style={styles.alt_text_md}>
Waiting for Opponent to Join...
</Text>
</View>
</ImageBackground>
);
}
}
I expect the event listener to execute from the current screen's componentDidMount() function, but it only executes if it's inside the previous screen's componentDidMount()
When you create a component,
the constructor -> componentWillMount -> render -> componentDidMount is
followed.
In your Lobby class, the event listener is run because it is in ComponentDidmont.
However, the event listener of the Gameroom class is inside the constructor. If executed within the constructor, the event cannot be heard because it is not yet rendered.
Event listeners are called when they appear on the screen
Usage
componentDidMount() {
this.navigationEventListener = Navigation.events().bindComponent(this);
}
componentWillUnmount() {
// Not mandatory
if (this.navigationEventListener) {
this.navigationEventListener.remove();
}
}

Header still showing after set to null

I use react-navigation for my app, I have set it to null for the splash component, but it still shows when the splash component is showing please what may I be doing wrong. Search for your herbs... isn't meant to show.
I was thinking of putting a state as the header: this.state.header so the state will be reset after the splash component has finished executing, i.e this.setState({header: true}), is this possible
Splash.js
export default class Splash extends React.Component {
static navigationOptions = {
header: null
};
render() {
return (
<View style={styles.container}>
<Image source={require("../logo.png")} />
</View>
);
}
}
class Box extends React.Component {
render() {
return (
<TextInput
placeholder="Search for your herbs..."
underlineColorAndroid={"transparent"}
style={BackStyles.textBox}
/>
);
}
}
Home.js
export default class Home extends React.Component {
static navigationOptions = {
headerTitle: <Box />
};
constructor(props) {
super(props);
this.state = {
record: []
};
}
render() {
setTimeout(() => {
this.setState({ timePassed: true });
}, 4000);
if (!this.state.timePassed) {
return <Splash />;
} else {
const herbs = this.state.record.map(herb => (
<View key={herb.id} style={BackStyles.herb_box}>
<Image
style={BackStyles.image}
source={{ uri: `${herb.name.replace(/ /g, "")}` }}
/>
<View style={{ flexDirection: "column" }}>
<Text style={BackStyles.header}>{herb.name}</Text>
<Text style={BackStyles.sub}>{herb.bot}</Text>
</View>
</View>
));
return (
<View style={BackStyles.main}>
<View style={{ flex: 1 }}>
<ScrollView overScrollMode={"never"}>{herbs}</ScrollView>
</View>
</View>
);
}
}
}
App.js
import { createStackNavigator } from "react-navigation";
const RootStack = createStackNavigator(
{
Home: Home
},
{
initialRouteName: "Home",
navigationOptions: {
headerStyle: {
backgroundColor: "#fff"
},
headerTintColor: "#28B564",
headerTitleStyle: {
fontWeight: "bold"
}
}
}
);
export default class App extends Component {
render() {
return <RootStack />;
}
}
I have encountered a problem similar to yours before, I think you should implement the splash component inside the App.js, i.e perform the setting of state of the timepassed variable in APP.js. So after the timepassed is true, you then show <RootStack/>
import Home from './components/Home';
import Splash from './components/Splash';
import { createStackNavigator } from 'react-navigation';
const RootStack = createStackNavigator(
{
Home: Home
},
{
initialRouteName: 'Home',
navigationOptions: {
headerStyle: {
backgroundColor: '#fff',
},
headerTintColor: '#28B564',
headerTitleStyle: {
fontWeight: 'bold',
},
},
}
);
export default class App extends Component {
constructor(props){
super(props);
this.state = {
timePassed: false,
};
}
render() {
setTimeout(() => {
this.setState({timePassed: true})
}, 4000);
if (!this.state.timePassed) {
return <Splash/>;
} else {
return (
<RootStack/>
);
}
}
}
Inside Splash component do this
static navigationOptions = {
header: {
visible: false,
}
}
And in your App.js write this after initialRouteName: "Home" or inside navigationOptions (try both cases please)
headerMode: 'screen'

Categories

Resources