Navigated React Native component is not rendered properly after updating its parent's state - react-native

I have a parent component with Navigator and 2 child components.
In parent component I have a method that updates the height of a View it has, which I pass to child components, allowing them to refresh the state of the parent once they are mounted.
When I navigate directly to second child, then call this method in second child's componentDidMount, the second child is re-rendered properly.
However, when I navigate from first child to second, the second child is not re-rendered as expected.
Parent:
import React, { Component } from 'react';
import {
StyleSheet,
Text,
Navigator,
View
} from 'react-native';
import FirstChild from './FirstChild';
import SecondChild from './SecondChild';
export default class Parent extends Component {
constructor(props) {
super(props);
this.setViewHeight = this.setViewHeight.bind(this);
this.state = {
viewHeight : 200
}
}
renderScene(route, navigator) {
var child = route.name == 'FirstChild' ?
<FirstChild navigator={navigator} heightSetter={this.setViewHeight}/> :
<SecondChild navigator={navigator} heightSetter={this.setViewHeight}/>;
return (
<View style={styles.container}>
<View style={[styles.dynamicView, {height: this.state.viewHeight}]}/>
{child}
</View>
)
}
render() {
return (
<Navigator
ref='navigator'
initialRoute={{ name: 'FirstChild' }}
renderScene={(route,navigator)=>this.renderScene(route,navigator)}
/>
);
}
setViewHeight() {
this.setState({
viewHeight: 100
})
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignSelf: 'stretch',
justifyContent: 'flex-start',
alignItems: 'center',
},
dynamicView: {
alignSelf: 'stretch',
backgroundColor: 'rgba(100,100,200, 0.5)'
}
});
First Child:
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
TouchableOpacity
} from 'react-native';
export default class FirstChild extends Component {
constructor(props) {
super(props)
}
render() {
return (
<TouchableOpacity style={styles.child} onPress={() => this.props.navigator.push({name: 'SecondChild'}) }>
<View style={styles.child}>
<Text>FIRST</Text>
</View>
</TouchableOpacity>
);
}
}
const styles = StyleSheet.create({
child: {
height: 200,
alignSelf: 'stretch',
backgroundColor: 'rgba(200,100,100,0.5)'
}
});
Second Child:
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View
} from 'react-native';
export default class SecondChild extends Component {
constructor(props) {
super(props)
}
componentDidMount() {
this.props.heightSetter();
}
render() {
return (
<View style={styles.child}>
<Text>Second</Text>
</View>
);
}
}
const styles = StyleSheet.create({
child: {
height: 200,
alignSelf: 'stretch',
backgroundColor: 'rgba(200,100,100,0.5)'
}
});

The question you are putting is not clear actually. What is the behavior that you got? Without clear picture of that it's not possible to answer your question but can tell you one thing that is
Using the navigation which is built in to react native is little complex and when there are many scenes to go through it doesn't support at all.
You can use navigation library like react-native router flux, which will definitely help you solve the issue you have got.
Here is the link,
https://github.com/aksonov/react-native-router-flux

Related

What's causing React Native componentDidMount being called none stop, indefinitely?

Here is the App.js
import React, { Component } from 'react';
import { Platform, StyleSheet, Text, View } from 'react-native';
import MovieList from './MovieList';
export default class App extends Component {
render() {
return (
<View style={styles.container}>
<MovieList />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
}
});
Here is the MoveList.js
import React, { Component } from 'react';
import { Platform, StyleSheet, FlatList, ActivityIndicator, Text, View } from 'react-native';
export default class MovieList extends Component {
constructor(props){
super(props);
this.state ={ isLoading: true}
}
componentDidMount() {
console.log("componentDidMount")
return fetch('https://facebook.github.io/react-native/movies.json')
.then((response) => response.json())
.then((responseJson) => {
// console.log("responseJson: " + JSON.stringify(responseJson))
this.setState({
isLoading: false,
dataSource: responseJson.movies,
}, function(){
});
})
.catch((error) =>{
console.error(error);
});
}
render(){
if(this.state.isLoading){
return(
<View style={{flex: 1, padding: 20}}>
<ActivityIndicator/>
</View>
)
}
return(
<View style={styles.container}>
<FlatList
data={this.state.dataSource}
renderItem={({item}) => <Text>{item.title}, {item.releaseYear}</Text>}
keyExtractor={({id}, index) => id}
/>
<MovieList name='Valeera' />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
}
});
The code for downloading the movie is from the official getting started tutorial networking, the only differences is that I put the code in a new js file called MovieList and include it as a component in the App.js
With the above code, the componentDidMount is being called none stop, and printing the movie list indefinitely, and eventually the app will crash.
Isn't the componentDidMount supposed to be called only once? What's causing it to be called indefinitely? What did I do wrong in the above code?
You are having your component MovieList embedded in the render-method of the component itself. This is creating the endless loop. Probably you only need to remove it there as your FlatList seems complete.

React-Native Constructor

Please help me find what is wrong in the following React-Native code?
It says after constructor (props) should have ';' semicolon. I don't know if I declared it in the right way.
import React from 'react';
import { StyleSheet, TextInput, View } from 'react-native';
export default function App() {
constructor (props){
this.state = {
text: 'HI'
}
}
render () {
return (
<View style={styles.container}>
<TextInput style={styles.input}
placeholder = 'Enter Value...'
placeholderTextColor ='#E74292'
onChangeText = {(text) => {
this.setState({text})
}}
/>
</View>
);
}
const styles = StyleSheet.create({
container:{
flex:1,
backgroundColor:'#F4C724',
},
input :{
marginTop:30,
height:30,
width:30,
borderWidth:2,
padding:10,
borderRadius: 5,
borderColor:'#1287A5'
}
}
);
You should declare the component as class instead of function if you want a constructor:
import React from 'react';
import { StyleSheet, TextInput, View } from 'react-native';
export default class App {
constructor(props) {
this.state = {
text: 'HI'
};
}
render() {
return (
<View style={styles.container}>
<TextInput
style={styles.input}
placeholder="Enter Value..."
placeholderTextColor="#E74292"
onChangeText={text => {
this.setState({ text });
}}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F4C724'
},
input: {
marginTop: 30,
height: 30,
width: 30,
borderWidth: 2,
padding: 10,
borderRadius: 5,
borderColor: '#1287A5'
}
});
constructor only works in class based component so switch to class based component rather than . functional whihc is now.
import React from 'react';
import { StyleSheet, TextInput, View } from 'react-native';
export default class App extends React.Component{
constructor(props) {
this.state = {
text: 'HI'
};
}
render() {
return (
<View style={styles.container}>
<TextInput
style={styles.input}
placeholder="Enter Value..."
placeholderTextColor="#E74292"
onChangeText={text => {
this.setState({ text });
}}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F4C724'
},
input: {
marginTop: 30,
height: 30,
width: 30,
borderWidth: 2,
padding: 10,
borderRadius: 5,
borderColor: '#1287A5'
}
});
You need to study the difference between functional and class component.
Functional component is just a plain java-script function which also known as stateless component. They do not manage their own state or have access to the lifecycle methods.
for more please follow the link below:
https://medium.com/#Zwenza/functional-vs-class-components-in-react-231e3fbd7108
you can use useState , the dynamic function value loads to the initial variable on load
import React,{ useState } from 'react';
import { View, Text, Button, ImageBackground} from 'react-native';
export default function Home({navigation}){
//function getversion onload
const [initval,setInitval] = useState(()=>{ return '123456'});
return(
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text> {initval} </Text>
</View>
);
}
You cannot have a constructor() in functional components. You should either change function component to class component or go and check out the react doc about React Hooks. You are going to have a better understanding of the differences between react class components and react functional components.

ImageBackground component as a container of react-navigation router hiding all child components

I want to set a background image to all of the screens in my react native application,
I am using ImageBackground component on the top level of my components tree like that:
export default class App extends React.Component {
render(){
return(
<View style={{ flex: 1 }}>
<ImageBackground source={require('../assets/app-bg.png')} style={{width: '100%', height: '100%', flex: 1, zIndex: 0, resizeMode: 'cover' }}>
<Router />
</ImageBackground>
</View>)
}
}
and I have the child component which is the router from react-navigation
like that:
class LandingPage extends React.Component {
render(){
return(
<View style={{flex: 1, zIndex: 999}}>
<Text>here is landing page></Text>
</View>
)
}
}
const RouterNavigator = createAppContainer(createStackNavigator({
Landing: {
screen: Landing,
navigationOptions:{
header: null
}
}
}
export default class Router extends React.Component {
render() {
return <RouterNavigator style={{flex: 1}}/>
}
}
the problem is that the background image is being rendered but the child component LandingPage is being hidden even though it is being rendered too!
Just have a look at this example.Does this help you acheive what you were
trying to.
import * as React from 'react';
import { Text, View, StyleSheet, ImageBackground } from 'react-native';
import { Constants } from 'expo';
import AssetExample from './components/AssetExample';
import { createAppContainer, createStackNavigator } from 'react-navigation';
import { Card } from 'react-native-paper';
class LandingPage extends React.Component {
render() {
return (
<View>
<Text>here is landing page</Text>
</View>
);
}
}
const RouterNavigator = createAppContainer(
createStackNavigator(
{
LandingPage: {
screen: LandingPage,
navigationOptions: {
header: null,
},
},
},
{
mode: 'card',
transparentCard: true,
cardStyle: { backgroundColor: 'transparent' },
transitionConfig: () => ({
containerStyle: {
backgroundColor: 'transparent',
},
}),
initialRouteName: 'LandingPage',
}
)
);
export default class App extends React.Component {
render() {
return (
<ImageBackground
source={require('./bgimage.jpeg')}
style={{
flex: 1,
}}>
<RouterNavigator />
</ImageBackground>
);
}
}

React Native - invalid prop children

I've created a few components. One of them should allow nesting however it inexplicably does not (inexplicable because I couldn't find any posts with quite this problem)
There is one image required to run this (it can be replaced with anything)
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { View, StatusBar, StyleSheet, ImageBackground } from 'react-native';
export class PhonyStatusBar extends Component {
render () {
return (
<View style={styles.statusBar} />
);
}
}
export class HomeScreen extends Component {
static propTypes = {
children: PropTypes.string
}
render () {
return (
<View>
{this.props.children}
</View>
);
}
}
export class AppGrid extends Component {
render () {
return (
<View />
);
}
}
export default class App extends Component {
render() {
return (
<View>
{/* hide system status bar */}
<StatusBar hidden={true} />
<PhonyStatusBar />
{/* throw in our own status bar */}
<HomeScreen>
<View />
</HomeScreen>
</View>
);
}
}
const styles = StyleSheet.create({
backgroundImage: {
width:'100%',
height:'100%',
},
statusBar: {
width: '100%',
height: 25.33,
backgroundColor: 'rgba(255, 157, 0, 0.5)'
},
});
In react children is a built in prop, already defined by the library, for components. This is not a prop that you should be defining manually. See this for more detail: https://reactjs.org/docs/jsx-in-depth.html#children-in-jsx
Try removing:
static propTypes = {
children: PropTypes.string
}
from HomeScreen to resolve the issue.

NavigatorIOS not rendering initialroute

I pretty much copied and pasted the demo code from Facebook, but the initialRoute component does not render. Answers like setting flex:1 as suggested by similar questions didn't work for me. Any tips?
import React, { Component } from 'react';
import {
StyleSheet,
NavigatorIOS,
StatusBar,
AppRegistry,
View,
Text,
TouchableHighlight,
} from 'react-native';
import NativeThing from './components/ReactNative';
export default class ListsList extends Component {
render() {
return (
<NavigatorIOS
initialRoute={{
component: MyScene,
title: 'My Initial Scene',
}}
style={{flex: 1}}
/>
);
}
}
class MyScene extends Component {
_onForward = () => {
this.props.navigator.push({
title: 'Scene ' + nextIndex,
});
}
render() {
return (
<View>
<Text>Current Scene: { this.props.title }</Text>
<TouchableHighlight onPress={this._onForward}>
<Text>Tap me to load the next scene</Text>
</TouchableHighlight>
</View>
)
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
}
});
AppRegistry.registerComponent('ListsList', () => ListsList);
The navigation bar by default overlaps the content of MyScene component. This is the default behaviour of the NavigatorIOS with a translucent
navigation bar.
So you have two options:
Add style paddingTop: 64 to the View of MyScene.
Add property translucent={ false } to the NavigatorIOS