Am new to the React world , so am facing issues with communications between the components. We have a 3 step login screen , and each screen has a top banner image and main background image , also few links in the footer .and the only difference is that in each screen we will have different form fields. lets say in the first screen we will have user name and a button , on the second screen some label and a text fields , and in the last screen we will have password fields and a image .
So what am planning is that i want to create a Index component with the common elements like i said the header logo, and the main image , and the footer . and i want to pass the components of the 3 screens into that so that Index will render the passed components , so if i pass the first component it should display the header logo , main image ,footer and the first screen component .
This is the first component which is calling Index and passing the LoginForm ,but its not rendering the passed LoginForm component, it just display the images of the Index component only.
How can i display the passed LoginForm within the Index component ?
import { LoginForm } from "./shared-components/LoginForm";
import { Index } from "./shared-components/Index";
export class Login extends Component {
constructor(prop) {
super(prop);
this.state = { username: "", password: "", result: [], isLoading: false };
}
render() {
return <Index passedForm={LoginForm} />;
}
}
This is the Index component
import React from "react";
import { Image, ImageBackground } from "react-native";
import { Container, Content, View } from "native-base";
export class Index extends React.Component {
constructor(prop) {
super(prop);
this.state = { username: "", password: "", result: [], isLoading: false };
}
render() {
return (
<Container>
<Content>
<Image
source={require("../assets/finastra_logo.jpg")}
style={{
maxHeight: 150,
alignSelf: "center",
maxWidth: 215,
flex: 1
}}
/>
<ImageBackground
resizeMode="cover"
blurRadius={3}
source={require("../assets/frond.jpg")}
style={{ alignSelf: "stretch", height: 500, marginBottom: 20 }}
>
{this.props.passedForm}
</ImageBackground>
</Content>
</Container>
);
}
}
You could fix it by passing the <LoginForm /> component instead of LoginForm.
But this is a good use case for the children property:
In Login.js, you can render:
render() {
return (
<Index>
<LoginForm />
</Index>
);
}
Then you can display the form in Index.js using:
{this.props.children}
You can pass it to variable and then use that variable to render
const PassedForm = this.props.passedForm;
And then render it like you would usually render components
<PassedForm />
Related
I want to blur a picture gradually depending on the number of clicks of the button 'Blur picture'. For example: if the user clicks one time it will blur the pic a little, then the second click will blur a little more, and so on...
Is there a library that does this, else how can I achieve it?
I would like to do this in React-Native but if you know how to do it using another language, I am opened to any suggestions.
The Image component has a property called "blurRadius" which you can trigger on touch (you'll have to use a touchable container like TouchableHighlight if the target is an Image).
Check the following example which does exactly what you describe; I use the state to keep track of the blur level.
https://snack.expo.io/#danyalejandro/b38413
import React, { Component } from "react";
import { TouchableHighlight, Image, View } from "react-native";
class App extends Component {
state: {
radius: number
}
constructor(props){
super(props);
this.state = { radius: 0 };
}
_imagePressed() {
this.setState({ radius: this.state.radius + 4 });
}
render() {
return (
<View>
<TouchableHighlight onPress={this._imagePressed.bind(this)}>
<Image
blurRadius={this.state.radius}
style={{ width: 320, height: 240 }}
source={{
uri:
"https://4.img-dpreview.com/files/p/E~TS590x0~articles/3925134721/0266554465.jpeg"
}}
resizeMode="contain"
/>
</TouchableHighlight>
</View>
);
}
}
export default App;
I have saved one data inside one class of my react-native project. With saving data I start a new screen. In that screen I am retrieving that saved data. For that purpose, I call AsyncStorage.getItem('token') function inside the render function.
Here is the code for that class-
import React from 'react';
import { StyleSheet, Text, View, Image, AsyncStorage } from 'react-native';
import {Icon, Button, Container, Header, Content, Left} from 'native-base';
import CustomHeader from './CustomHeader';
class NoteMeHome extends React.Component {
state = {
text:'',
storedValue:'',
getValue: ''
};
static navigationOptions = ({navigation}) => ({
title: "Home",
headerLeft: <Icon name="ios-menu" style={{paddingLeft:10}}
onPress={()=>navigation.navigate('DrawerOpen')}/>,
drawerIcon:
<Image source={require('../assets/icon.png')}
style={styles.icon}
/>
})
render() {
const {storedValue} = this.state;
AsyncStorage.getItem('token').then(value =>
//AsyncStorage returns a promise so adding a callback to get the value
this.setState({ getValue: value })
//Setting the value in Text
);
return(
<Container>
<CustomHeader
title="Home"
drawerOpen={()=>this.props.navigation.navigate('DrawerOpen')}
/>
<Content contentContainerStyle={{flex:1, alignItems:'center',
justifyContent:'center', padding:10}}>
<Button full onPress={()=> this.props.navigation.navigate('Settings')}>
<Text style={{color:'white'}}>{storedValue}</Text>
</Button>
<Text>{this.state.getValue}</Text>
</Content>
</Container>
)
}
}
const styles = StyleSheet.create({
icon:{
height: 24,
width: 24
}
})
export default NoteMeHome;
After that, while running the project, in the above mentioned class, when I click any of my drawer items to go to another screen, it shows the following error in the console-
wanrning: can't call setState(or forceUpdate) on an unmounted
component. This is no-op, but it indicates a memory leak in your
application. To fix, cancel all subscriptions and asyncshronous tasks
in the componentWillUnmount method.
I guess something goes wrong with AsyncStorage.getItem('token') function calling because if I remove the function it doesn't show any warning.
So, it would be very nice if someone helps me to know where should I call the following code-
AsyncStorage.getItem('token').then(value =>
//AsyncStorage returns a promise so adding a callback to get the value
this.setState({ getValue: value })
//Setting the value in Text
);
to remove the warning ?
Asif,
Here is what I had in mind :
import React from 'react';
import { StyleSheet, Text, View, Image, AsyncStorage } from 'react-native';
import {Icon, Button, Container, Header, Content, Left} from 'native-base';
import CustomHeader from './CustomHeader';
class NoteMeHome extends React.PureComponent {
state = {
text:'',
storedValue:'',
getValue: ''
};
static navigationOptions = ({navigation}) => ({
title: "Home",
headerLeft: <Icon name="ios-menu" style={{paddingLeft:10}}
onPress={()=>navigation.navigate('DrawerOpen')}/>,
drawerIcon:
<Image source={require('../assets/icon.png')}
style={styles.icon}
/>
});
componentDidMount() {
const token = await AsyncStorage.getItem('token');
this.setState({ getValue: token });
}
render() {
const {storedValue, getValue} = this.state;
return(
<Container>
<CustomHeader
title="Home"
drawerOpen={()=>this.props.navigation.navigate('DrawerOpen')}
/>
<Content contentContainerStyle={{flex:1, alignItems:'center',
justifyContent:'center', padding:10}}>
<Button full onPress={()=> this.props.navigation.navigate('Settings')}>
<Text style={{color:'white'}}>{storedValue}</Text>
</Button>
<Text>{getValue}</Text>
</Content>
</Container>
)
}
}
const styles = StyleSheet.create({
icon:{
height: 24,
width: 24
}
})
export default NoteMeHome;
I don't know if in your case you should actually try to handle the update of the component since you don't have any props.
Regards,
IMO you should use componentDidMount for anything that you want to do in the beginning of a screen. To use AsyncStorage remember that is an asynchronous function so you have to wait for the function to complete so you can get the value.
For more information about react native's components please see this
For more information about 'waiting' for AsyncStorage using async please see this examples
In react component render() should always remain pure. One should never set state in render function, it a very bad pracice, in a simple component it might work fine. It only works because of the asynchronicity.
You should use componentDidMount lifecycle method to fetch data from local storage.
componentDidMount() {
const token = await AsyncStorage.getItem('token');
this.setState({ getValue: token });
}
I'm new to ReactNative and started to using TabBarIOS component for a project. I have TabBarIOS component which has 5 different TabBarIOS.Item Component. These each all point another component to present. These different components are all have different backgroundColor's and styles and titles but when I change the selectedTab the change has happened but the properties of components such as backgroundColor not affect the presented component. For testing, I've log a text in componentWillMount method of the Component class for each one. And they logged successfully. Here is the partial components. For the first Component which is named as Restaurants the title is correctly showing in navigationItem but in others navigationItem's title is empty.
I've called my components as ViewControllers.
class RestaurantsComponent extends Component{
componentWillMount(){
console.log('restauranscomponent will mounted');
}
render(){
return(
<View style={{flex:1, backgroundColor:'blue'}}>
<Text>ASDFSADF</Text>
</View>
)
}
}
class SearchViewController extends Component{
componentWillMount(){
console.log('search view controller will mounted');
}
render(){
return(
<View style={{flex:1, backgroundColor:'green'}}>
<Text>askfkjasljkdfjkla</Text>
</View>
)
}
}
etc..
Here is main tabbar Component class:
export default class SimpleClass extends Component{
constructor(props){
super(props);
this.state = {
selectedTab: 'news'
}
}
changeTab(selectedTab){
this.setState({selectedTab})
}
render(){
const { selectedTab } = this.state
const styles = {
backgroundColor: 'red'
};
return(
<TabBarIOS barTintColor="white"
unselectedItemTintColor="gray"
tintColor="red"
style={{flex:1}}
>
<TabBarIOS.Item
selected={selectedTab === 'news'}
title="Restaurants"
icon={require('./assets/restaurants.png')}
onPress={() => this.changeTab('news')}
>
<NavigatorIOS
style={styles.nav}
initialRoute={{
component: RestaurantsComponent,
title : 'Restaurants'
}}
/>
</TabBarIOS.Item>
<TabBarIOS.Item
title="Search"
selected={selectedTab === 'news2'}
onPress={() => this.changeTab('news2')}
icon={require('./assets/searchIco.png')}
>
<NavigatorIOS
style={styles.nav}
initialRoute={{
component: AnotherComponent,
title : 'Search'
}}
/>
</TabBarIOS.Item>
...
.../>
Here is the Component in navigationItem for Restaurants
And for other else:
I'vent cut the tabBar item for the screenshot but the TabBarIOS is successfully works if you mind it.
Is there any bug which is currently which cause from me or what happens to navigationItem's title attributes?
I've found my answer by the way I've not figured out what was happening in here but when looking into documentation and some articles, the use of NavigatorIOS is currently making mess.And there is a cool question & answer that I think its important to get idea of createNavigator... .
Here is the link.
There is a close approach for using TabBar, Navigation etc which are named createStackNavigator and createBottomTabNavigator. As the names tell us, createStackNavigator is currently work like UINavigationController and also createBottomTabNavigator is working like UITabBarController. So this is the basic implementation of these approach.
const firstTabStack = createStackNavigator({
HomeAlways: {
navigationOptions:{
title:'WASSUP1'
},
screen:BooksNav
}
})
const secondTabStack = createStackNavigator({
HelpAlways: {
navigationOptions:{
title:'WASSUP2'
},
screen:AddBook
}
})
And finally here we come with Tab implementation.
const Tab = createBottomTabNavigator({
Home: {
screen: firstTabStack,
navigationOptions:{
title:'title1'
}
},
Another: {
screen: secondTabStack,
navigationOptions:{
title:'title2'
}
}
});
What did I do with these code?
For iOS Developers to get understand what is going on in there, there is a 2 Controller (UIViewController or Component in RN), and these have different UINavigationController's and also different titles. And all of these controllers will going to stack of the UITabBarController' viewControllers.
The images in below are proof of the successfully running.
,
Im following this tutorial https://reactnavigation.org/docs/intro/ and im running into a bit of issues.
Im using the Expo Client app to render my app every time and not a simulator/emulator.
my code is seen down below.
I originally had the "SimpleApp" const defined above "ChatScreen" component but that gave me the following error:
Route 'Chat' should declare a screen. For example: ...etc
so I moved the decleration of SimpleApp to just above "AppRegistry" and that flagged a new error
Element type is invalid: expected string.....You likely forgot to export your component..etc
the tutorial did not add the key words "export default" to any component which I think it may have to do with the fact that im running it on the Expo app? so I added "export default" to "HomeScreen" and the error went away.
The new error that I cant seem to get rid off(based on the code below) is the following:
undefined is not an object (evaluating 'this.props.navigation.navigate')
I can't get rid of it unless I remove the "{}" around "const {navigate}" but that will break the navigation when I press on the button from the home screen
import React from 'react';
import {AppRegistry,Text,Button} from 'react-native';
import { StackNavigator } from 'react-navigation';
export default class HomeScreen extends React.Component {
static navigationOptions = {
title: 'Welcome',
};
render() {
const { navigate } = this.props.navigation;
return (
<View>
<Text>Hello, Chat App!</Text>
<Button
onPress={() => navigate('Chat')}
title="Chat with Lucy"
/>
</View>
);
}
}
class ChatScreen extends React.Component {
static navigationOptions = {
title: 'Chat with Lucy',
};
render() {
return (
<View>
<Text>Chat with Lucy</Text>
</View>
);
}
}
const SimpleApp = StackNavigator({
Home: { screen: HomeScreen },
Chat: { screen: ChatScreen },
});
AppRegistry.registerComponent('SimpleApp', () => SimpleApp);
Additional Info:
When you are nesting child components, you need to pass navigation as prop in parent component.
//parent.js
<childcomponent navigation={this.props.navigation}/>
And you can access navigation like this
//child.js
this.props.navigation.navigate('yourcomponent');
Reference: https://reactnavigation.org/docs/en/connecting-navigation-prop.html
With Expo you should't do the App registration your self instead you should let Expo do it, keeping in mind that you have to export default component always:
Also you need to import View and Button from react-native: please find below the full code:
import React from 'react';
import {
AppRegistry,
Text,
View,
Button
} from 'react-native';
import { StackNavigator } from 'react-navigation';
class HomeScreen extends React.Component {
static navigationOptions = {
title: 'Welcome',
};
render() {
const { navigate } = this.props.navigation;
return (
<View>
<Text>Hello, Chat App!</Text>
<Button
onPress={() => navigate('Chat', { user: 'Lucy' })}
title="Chat with Lucy"
/>
</View>
);
}
}
class ChatScreen extends React.Component {
// Nav options can be defined as a function of the screen's props:
static navigationOptions = ({ navigation }) => ({
title: `Chat with ${navigation.state.params.user}`,
});
render() {
// The screen's current route is passed in to `props.navigation.state`:
const { params } = this.props.navigation.state;
return (
<View>
<Text>Chat with {params.user}</Text>
</View>
);
}
}
const SimpleAppNavigator = StackNavigator({
Home: { screen: HomeScreen },
Chat: { screen: ChatScreen }
});
const AppNavigation = () => (
<SimpleAppNavigator />
);
export default class App extends React.Component {
render() {
return (
<AppNavigation/>
);
}
}
As Bobur has said in his answer, the navigation prop isn't passed to children of the routed component. To give your components access to navigation you can pass it as a prop to them, BUT there is a better way.
If you don't want to pass the navigation prop all the way down your component hierarchy, you can use useNavigation instead. (Which in my opinion is just cleaner anyways, and reduces the amount of code we have to write):
function MyBackButton() {
const navigation = useNavigation();
return (
<Button
title="Back"
onPress={() => {
navigation.goBack();
}}
/>
);
}
https://reactnavigation.org/docs/use-navigation/
This is just really nice because if you have multiple levels of components you wont have to continuously pass the navigation object as props just to use it. Passing navigation just once requires us to 1. Add a prop to the component we want to pass it to. 2. Pass the prop from the parent component. 3. Use the navigation prop to navigate. Sometimes we have to repeat steps 1 and 2 to pass the prop all the way down to the component that needs to use navigation. We can condense steps 1 and 2, no matter how many times they are repeated, into a single useNavigation call with this method.
I think it is best.
Try this Code: onPress={() => this.props.navigation.navigate('Chat')}
<ChildComponent navigation={props.navigation} {...data} />
This will make the navigation from the parent propagated to the subsequent child navigations.
const AppNavigation =()=>{ <SimpleApp />}
export default class App extends React.Componet{
render(){
return (
<AppNavigation/>
);
}
}
I am receiving null is not an object (evaluating 'this.state.search') I am new to react-native so not 100% sure whats going on. thanks for any help.
This is my basic search bar:
use strict';
import React, { Component } from 'react';
import { StyleSheet, Text, View, TextInput, Button } from 'react-native';
import renderIf from '../common/renderIf';
import { Container, Header, Title, Content, Icon, CardItem, Card, Input, InputGroup} from 'native-base';
export default class SearchBar extends Component {
render() {
return (
<Card>
<CardItem searchBar rounded>
<InputGroup>
<Icon name="ios-search" />
<Input placeholder="Search" value={this.state.search} onChangeText={(text) => this.setState({search:text})} onSubmitEditing={()=>this.search()}/>
</InputGroup>
</CardItem>
</Card>
);
}
}
This is is to take the inputed text from the search bar and display the results:
'use strict';
import React, { Component } from 'react';
import { StyleSheet, Text, View, TextInput, Button } from 'react-native';
import renderIf from '../common/renderIf';
import { Container, Content, Icon, CardItem, Card, Thumbnail, Title, List, ListItem} from 'native-base';
import { Col, Row, Grid } from "react-native-easy-grid";
export default class AddMovieResults extends Component {
search() {
// Set loading to true when the search starts to display a Spinner
this.setState({
loading: true
});
var that = this;
return fetch('http://www.omdbapi.com/?s=' +this.state.search)
.then((response) => response.json())
.then((responseJson) => {
// Store the results in the state variable results and set loading to
// false to remove the spinner and display the list of repositories
that.setState({
results: responseJson,
loading: false
});
return responseJson.Search;
})
.catch((error) => {
that.setState({
loading: false
});
console.error(error);
});
}
render() {
return (
<Card scrollEnabled={true}>
<CardItem button >
<List dataArray={this.state.results.items} renderRow={(item) =>
<ListItem button >
<Row>
<Col size={1}>
<Thumbnail square style={{ height: 90, width:60, bottom:6,justifyContent: 'center',}} source={{uri: item.poster}} />
</Col>
<Col size={3}>
<Row size={3}>
<Text style={{ fontSize: 25, color: '#DD5044',justifyContent: 'center',}}>{item.title}</Text>
</Row>
<Row size={1}>
<Text style={{ fontSize: 15, color: '#DD5044',}}>{item._year_data}</Text>
</Row>
</Col>
</Row>
</ListItem>
} />
</CardItem>
</Card>
);
}
}
This is my index file which displays the above files on one page:
'use strict';
import React, { Component } from 'react';
import { StyleSheet, Text, View, TextInput, Button } from 'react-native';
import renderIf from '../common/renderIf';
import { Container, Header, Title, Content} from 'native-base';
import { Col, Row, Grid } from "react-native-easy-grid";
import AddMovieResults from './AddMovieResults';
import SearchBar from './SearchBar';
export default class AddNewMovie extends Component {
render() {
return (
<Container scrollEnabled={false}>
<Header>
<Title> Add Movie</Title>
</Header>
<Content>
<Grid>
<Col>
{/* import search bar */}
<SearchBar/>
{/*import search results*/}
<AddMovieResults/>
</Col>
</Grid>
</Content>
</Container>
);
}
}
State is not global, it is local to each component so you will need to pass it to the object as a prop.
The problem here however is that as you are defining the search bar and add movie results you will need to find a way to pass the state back from SearchBar.
To accomplish this you can pass a reference function to update the state of AddNewMovie:
Add the following function to your addNewMovie class:
updateAddNewMovieState = (newData) => {
this.setState({search:newData})
}
Next pass it into the search bar class:
<SearchBar
updateState = {this.updateAddNewMovieState}
currentState = {this.state.search}
/>
Now use this.props.currentState to access the search state and this.props.updateState(newState) to modify the state in AddNewMovie from the search bar class.
Finally, pass the variable through to AddMovieResults:
<AddMovieResults
search={this.state.search}
/>
You can then access the variable in AddMovieResults via this.props.search.
While this method is relatively straight forward it quickly becomes convoluted if you are passing many variables around, for this purpose I recommend https://github.com/reactjs/react-redux which allows you to store variables much cleaner through action functions and reduction states.
I would also recommend defining your state variables in each component constructor to make it much clearer where they are being defined:
constructor(props) {
super(props);
this.state = {
something: "Something"
};
}
you need to bind your function in the constructor to be able to access this outside of inherited React class functions like render, constructor, etc...:
export default class AddMovieResults extends Component {
constructor(props){
super(props);
this.search = this.search.bind(this);
}
search() {
// Set loading to true when the search starts to display a Spinner
this.setState({
loading: true
});
}
...
...
}