React-native Android Navigator -scene blank - android

I try to make a example of navigator ,but the scene is always blank.Some advices told me to set flex:1 to the navigator,but it doesn't work!
Here is my code:
index.android.js:
'use strict';
import React, {
AppRegistry,
Component,
StyleSheet,
Navigator,
Text,
View
} from 'react-native';
import Homepage from './Homepage' ;
class NavigatorTest extends Component {
render() {
var defaultName = 'Homepage' ;
var defaultCom = Homepage ;
return (
<Navigator style = {{flex:1}}
initialRoute = {{name: defaultName,component:Homepage }}
configureScene = {() => {
return Navigator.SceneConfigs.VerticalDownSwipeJump ;
}}
renderScene = {(route,navigator) => {
let Component = route.component ;
if (route.component) {
return <Component {...route.params} navigator = {navigator} />
}
}} />
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
});
AppRegistry.registerComponent('NavigatorTest', () => NavigatorTest);
Homepage.js
import React, {
Component,
StyleSheet,
TouchableOpacity,
Text,
View
} from 'react';
import Detailpage from './Detailpage' ;
export class Homepage extends React.Component {
static propTypes = {
};
constructor(props) {
super(props);
}
_pressButton() {
const {navigator} = this.props ;
if(navigator) {
navigator.push({
name: 'Detailpage',
component:Detailpage,
})
}
}
render() {
return (
<View>
<TouchableOpacity onPress = {this._pressButton}>
<Text>click to jump to Detailpage!</Text>
</TouchableOpacity>
</View>
);
}
}
Detailpage.js
import React, {
Component,
StyleSheet,
TouchableOpacity,
Text,
View
} from 'react';
import Homepage from './Homepage' ;
export class Detailpage extends React.Component {
static propTypes = {
};
constructor(props) {
super(props);
}
_pressButton() {
const {navigator} = this.props ;
if (navigator) {
navigator.pop() ;
}
}
render() {
return (
<View>
<TouchableOpacity onPress = {this._pressButton}>
<Text>click to get back</Text>
</TouchableOpacity>
</View>
);
}
}
I'm a newcomer of react-native ,please give me some advice ,thanks in advance!

Try simplifying and separating your Navigator logic a bit. Debugging will also be easier this way.
class NavigatorTest extends Component {
render() {
return (
<Navigator
initialRoute={{id: 'Homepage'}}
configureScene={this.configureScene}
renderScene={this.renderScene}
/>
);
}
configureScene() {
return Navigator.SceneConfigs.VerticalDownSwipeJump;
}
renderScene(route, navigator) {
switch(route.id) {
case 'Homepage':
return this.renderHomepage(navigator);
case 'Detailpage':
return this.renderDetailpage(navigator);
default:
throw new Error('No route found for id ' + route.id);
}
}
renderHomepage(navigator) {
return <Homepage navigator={navigator} />;
}
renderDetailpage(navigator) {
return <Detailpage navigator={navigator} />;
}
}
Homepage.js
...
_pressButton() {
this.props.navigator.push({id: 'Detailpage'});
}
...

Separate the Navigator logic as Villeaka proposed is much more clear for reading and analysing.
The method "this.renderHomepage(navigator)" was called in "renderScene" scope, so "this" in it refer to "renderScene" not the NavigatorTest component, pass "this" to a new variable like below:
renderScene(route, navigator) {
var me = this;
switch(route.id) {
case 'Homepage':
return me.renderHomepage(navigator);
case 'Detailpage':
return me.renderDetailpage(navigator);
default:
throw new Error('No route found for id ' + route.id);
}}

I think your navigation look a little messed up, I found this video make it so easy
https://www.youtube.com/watch?v=jGOst2TLha0
By the way, my quick though on your code is
<Navigator style = {{flex:1}}
initialRoute = {{name: defaultName,component:Homepage }}
configureScene = {() => {
return Navigator.SceneConfigs.VerticalDownSwipeJump ;
}}
renderScene = {(route,navigator) => {
let Component = route.component ;
if (route.component) {
return <Component {...route.params} navigator = {navigator} />
}
}} />
That "let Component" doesn't look right because variable name should not be same as "Component" library that imported on the top of your code, cause ambiguous

Related

Button Not Responding problem with async React Native

I copied fallowing code from a github project and tried using expo. The project executed without error but when i press button nothing happens. not even error this is my code
NB- I stetted an alert inside onChooseImagePress and alert is working fine
import React from 'react';
import { Image, StyleSheet, Button, Text, View, Alert, } from 'react-native';
import { ImagePicker } from 'expo';
import * as firebase from 'firebase';
import {firebaseConfig} from "./ApiKeys";
export default class HomeScreen extends React.Component {
static navigationOptions = {
header: null,
};
onChooseImagePress = async () => {
let result = await ImagePicker.launchCameraAsync();
//let result = await ImagePicker.launchImageLibraryAsync();
if (!result.cancelled) {
this.uploadImage(result.uri, "test-image")
.then(() => {
Alert.alert("Success");
})
.catch((error) => {
Alert.alert(error);
});
}
}
uploadImage = async (uri, imageName) => {
const response = await fetch(uri);
const blob = await response.blob();
var ref = firebase.storage().ref().child("images/" + imageName);
return ref.put(blob);
}
render() {
return (
<View style={styles.container}>
<Button title="Choose image..." onPress={this.onChooseImagePress} />
</View>
);
}
}
const styles = StyleSheet.create({
container: { flex: 1, paddingTop: 50, alignItems: "center", },
});
}
Multiple syntactical issues in your code:
const styles... should be defined inside the render function currently its dangling outside the class
Brackets mismatch
return (
<View style={styles.container}>
<Button title="Choose image..." onPress={this.onChooseImagePress} />
</View>
);
}
} // the class ends here
Please let me know if it still doesn't work
Try to use below code
constructor() {
super();
this.state = { };
this.onChooseImagePress= this.onChooseImagePress.bind(this);
}
<Button title="Choose image..." onPress={() => this.onChooseImagePress()} />

Expo Camera recordAsync promise not resolving

Im starting to develop a mobile application with expo/react native, but I'm having some problems handling the camera object:
I have a camera object that I start recording (recordAsync) at componentDidMount and I stop it (stopRecording) at componentWillUnmount. however the promise is never resolved (neither the then, catch no finally are called)
am I doing something wrong?
here's the code:
import { Camera, Permissions } from 'expo';
import React from 'react';
export default class CameraReaction extends React.Component {
constructor(props){
super(props)
this.takeFilm = this.takeFilm.bind(this)
this.isFilming=false
this.cameraScreenContent = this.renderCamera()
}
componentDidMount(){
if (this.props.shouldrecording && !this.isFilming ){
this.takeFilm()
}
}
componentWillUnmount(){
this.camera.stopRecording()
}
saveMediaFile = async video => {
console.log("=======saveMediaFile=======");
}
renderCamera = () => {
let self = this
return (
<View style={{ flex: 1 }}>
<Camera
ref={ref => {self.camera=ref}}
style={styles.camera}
type='front'
whiteBalance='off'
ratio='4:3'
autoFocus='off'
>
</Camera>
</View>
);
}
takeFilm(){
let self = this
try{
self.camera.recordAsync()
.then(data => {
self.saveMediaFile(data),
self.isFilming=false
})
.catch(error => {console.log(error)})
this.isFilming = true
}
catch(e){
this.isFilming = false
}
};
render() {
return <View style={styles.container}>{this.cameraScreenContent}</View>;
}
}
anyone has any clue of what I'm doing wrong?
thanks in advance
I finally realised that we can't start recording directly when a component is rendered. An by 'directly' I mean without any further action from the user. If I do it in two steps (p.e. waiting for the user to click somewhere), if works perfectly. But I don't see any reference to this behaviour / limitation in the documentation.
The working code bellow:
import React from 'react';
import { StyleSheet, Text, View , TouchableOpacity} from 'react-native';
import { Camera, Permissions} from 'expo';
export default class App extends React.Component {
constructor(props){
super(props)
this.camera=undefined
this.state = {permissionsGranted:false,bcolor:'red'}
this.takeFilm = this.takeFilm.bind(this)
}
async componentWillMount() {
let cameraResponse = await Permissions.askAsync(Permissions.CAMERA)
if (cameraResponse.status == 'granted'){
let audioResponse = await Permissions.askAsync(Permissions.AUDIO_RECORDING);
if (audioResponse.status == 'granted'){
this.setState({ permissionsGranted: true });
}
}
}
takeFilm(){
let self = this;
if (this.camera){
this.camera.recordAsync().then(data => self.setState({bcolor:'green'}))
}
}
render() {
if (!this.state.permissionsGranted){
return <View><Text>Camera permissions not granted</Text></View>
} else {
return (
<View style={{flex: 1}}>
<View style={{ flex: 1 }}>
<Camera ref={ref => this.camera = ref} style={{flex: 0.3}} ></Camera>
</View>
<TouchableOpacity style={{backgroundColor:this.state.bcolor, flex:0.3}} onPress={() => {
if(this.state.cameraIsRecording){
this.setState({cameraIsRecording:false})
this.camera.stopRecording();
}
else{
this.setState({cameraIsRecording:true})
this.takeFilm();
}
}} />
</View>)
}
}
}

Global in-app notifications

I wish to utilize an in-app notification system, aka a more attractive and less in your face' use of alerts to let the user know what actions are being done, especially when for instance a barcode has been detected but it needs to send that barcode to the server and the user needs to wait.
I have found this lib and have attempted to implement it; but as I am using React Navigation and I wish to render the item at the very top of the application, it gets cut off by React Native header
Is it possible to have a function I can create and reference whenever I want a global notification and it will render on the very top I would imagine it would need to render here:
import React from 'react';
import { createBottomTabNavigator,createStackNavigator } from 'react-navigation';
import SearchTab from './components/Tabs/SearchTab';
import HomeTab from './components/Tabs/HomeTab';
import ScannerTab from './components/Tabs/ScannerTab';
import SettingsTab from './components/Tabs/SettingsTab';
import Ionicons from 'react-native-vector-icons/Ionicons';
import StockModal from './components/Modals/StockModal';
const MainStack = createBottomTabNavigator(
{
Home: HomeTab,
Search: SearchTab,
Scanner: ScannerTab,
Settings: SettingsTab,
//Todo: Total overlay modals HERE
},
{
navigationOptions: ({ navigation }) => ({
tabBarIcon: ({ focused, tintColor }) => {
const { routeName } = navigation.state;
let iconName;
if (routeName === 'Home') {
iconName = `ios-information-circle${focused ? '' : '-outline'}`;
} else if (routeName === 'Settings') {
iconName = `ios-options${focused ? '' : '-outline'}`;
}else if (routeName === 'Scanner') {
iconName = `ios-barcode${focused ? '' : '-outline'}`;
}else if (routeName === 'Search') {
iconName = `ios-search${focused ? '' : '-outline'}`;
}
return <Ionicons name={iconName} size={25} color={tintColor} />;
},
}),
tabBarOptions: {
activeTintColor: 'tomato',
inactiveTintColor: 'gray',
},
}
);
export default RootStack = createStackNavigator(
{
Main: {
screen: MainStack,
},
QuickStockScreen: {
screen: StockModal,
},
},
{
mode: 'modal',
headerMode: 'none',
}
);
But even if that's possible, I am not sure how its possible to build a function that tells the notification to show; React Redux comes to mind but I don't wish to implement such a cumbersome system just for one feature and it was something I considered when creating his application and decided against.
The notification system in question (not very clear documentation or examples sadly) https://www.npmjs.com/package/react-native-in-app-notification
Here is the navigation lib I am using: https://reactnavigation.org/
What you want would be a component that is a the same level of the navigation (So it can display over it). In multiple projects, I use react-native-root-siblings to do so. It allows you to add UI over the app and so over the navigation.
An exemple how what I made with it. The dark layer and the box at the bottom are part of the Siblings Component.
https://gyazo.com/7ad3fc3fea767ea84243aaa493294670
The Siblings is used like the Alert of React-Native, so as a function (which is quite useful!)
messageMenu.js
import React, { Component } from 'react';
import RootSiblings from 'react-native-root-siblings';
import MessageMenuContainer from './MessageMenuContainer';
export default class Dialog extends Component {
static show = (props) => new RootSiblings(<MessageMenuContainer {...props} />);
static update = (menu, props) => {
if (menu instanceof RootSiblings) {
menu.update(<MessageMenuContainer {...props} />);
} else {
console.warn(`Dialog.update expected a \`RootSiblings\` instance as argument.\nBut got \`${typeof menu}\` instead.`);
}
}
static close = (menu) => {
if (menu instanceof RootSiblings) {
menu.destroy();
} else {
console.warn(`Dialog.destroy expected a \`RootSiblings\` instance as argument.\nBut got \`${typeof menu}\` instead.`);
}
}
render() {
return null;
}
}
export {
RootSiblings as Manager,
};
Where the MessageMenuContainer is your component to render at the top.
Component using the Root Siblings:
import React from 'react';
import PropTypes from 'prop-types';
import I18n from 'react-native-i18n';
import { BackHandler, Keyboard, Platform, TouchableOpacity } from 'react-native';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import DraftMenu from './messageMenu'; //HERE IS THE IMPORT YOU WANT
import { Metrics, Colors, Fonts } from '../../main/themes';
class DraftBackButton extends React.Component {
state = {
draftMenu: undefined,
}
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackAndroid);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackAndroid);
}
handleBackAndroid = () => {
this.handleBack();
return true;
}
handleBack = async () => {
Keyboard.dismiss();
await this.openDraftMenu();
}
openDraftMenu = async () => {
if (this.state.draftMenu) {
await DraftMenu.update(this.state.draftMenu, this.draftMenuProps());
} else {
const draftMenu = await DraftMenu.show(this.draftMenuProps());
this.setState({ draftMenu: draftMenu });
}
}
draftMenuProps = () => ({
options: [
{ title: I18n.t('message.deleteDraft'), onPress: this.deleteDraft, icon: 'trash' },
{ title: I18n.t('message.saveDraft'), onPress: this.saveOrUpdateDraft, icon: 'documents' },
{ title: I18n.t('cancel'), icon: 'close', style: { backgroundColor: Colors.tertiaryBackground } },
],
destroyMenuComponent: async () => {
DraftMenu.close(this.state.draftMenu);
await this.setState({ draftMenu: undefined });
},
withIcon: true,
})
saveOrUpdateDraft = async () => {
// SAVE OR UPDATE DRAFT. NOT IMPORTANT
}
saveDraft = async () => {
// SAVING THE DRAFT
}
updateDraft = async () => {
// UPDATING THE DRAFT
}
deleteDraft = async () => {
// DELETING THE DRAFT
}
render() {
return (
<TouchableOpacity
hitSlop={Metrics.touchable.largeHitSlop}
onPress={() => {
this.handleBack();
}}
>
<Text>BUTTON</Text>
</TouchableOpacity>
);
}
}
DraftBackButton.propTypes = {
// ALL THE PROPTYPES
};
function mapStateToProps(state, ownProps) {
//
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({ fetchMessages }, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(DraftBackButton);
The best thing with this lib is that you can call the .show anywhere in your app and it will render at the very top!
Hope it's what you're looking for!
EDIT:
I updated the example of how to use the Root Siblings.
Here's the content of my MessageContainer which will be display on top of everything
import React from 'react';
import PropTypes from 'prop-types';
import { Animated, Dimensions, InteractionManager, StyleSheet, TouchableOpacity, View } from 'react-native';
import MessageMenuItem from './MessageMenuItem';
import { Colors } from '../../../main/themes';
const { width, height } = Dimensions.get('window');
const OPTION_HEIGHT = 55;
const OVERLAY_OPACITY = 0.5;
export default class DraftMenuContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
animatedHeight: new Animated.Value(0),
animatedOpacity: new Animated.Value(0),
menuHeight: props.options.length * OPTION_HEIGHT,
};
}
componentDidMount() {
this.onOpen();
}
// Using Animated from react-native to make the animation (fade in/out of the dark layer and the dimensions of the actual content)
onOpen = async () => {
await this.state.animatedHeight.setValue(0);
await this.state.animatedOpacity.setValue(0);
Animated.parallel([
Animated.timing(this.state.animatedHeight, { toValue: this.state.menuHeight, duration: 200 }),
Animated.timing(this.state.animatedOpacity, { toValue: OVERLAY_OPACITY, duration: 200 }),
]).start();
}
onClose = async () => {
await this.state.animatedHeight.setValue(this.state.menuHeight);
await this.state.animatedOpacity.setValue(OVERLAY_OPACITY);
Animated.parallel([
Animated.timing(this.state.animatedHeight, { toValue: 0, duration: 200 }),
Animated.timing(this.state.animatedOpacity, { toValue: 0, duration: 200 }),
]).start(() => this.props.destroyMenuComponent()); // HERE IS IMPORTANT. Once you're done with the component, you need to destroy it. To do so, you need to set a props 'destroyMenuComponent' which is set at the creation of the initial view. See the other code what it actually do
}
render() {
return (
<View style={styles.menu}>
<Animated.View style={[styles.backgroundOverlay, { opacity: this.state.animatedOpacity }]}>
<TouchableOpacity
activeOpacity={1}
onPress={() => this.onClose()}
style={{ flex: 1 }}
/>
</Animated.View>
<Animated.View style={[styles.container, { height: this.state.animatedHeight }]}>
{this.props.options.map((option, index) => (
<MessageMenuItem
height={OPTION_HEIGHT}
icon={option.icon}
key={index}
onPress={async () => {
await this.onClose();
InteractionManager.runAfterInteractions(() => {
if (option.onPress) {
option.onPress();
}
});
}}
style={option.style}
title={option.title}
withIcon={this.props.withIcon}
/>
))}
</Animated.View>
</View>
);
}
}
DraftMenuContainer.propTypes = {
destroyMenuComponent: PropTypes.func.isRequired,
withIcon: PropTypes.bool,
options: PropTypes.arrayOf(PropTypes.shape({
icon: PropTypes.string.isRequired,
onPress: PropTypes.func,
title: PropTypes.string.isRequired,
})),
};

How to make search filter with list item in react-native

I have been wanting to make search filter in this listitem but i kind of get confused, if you are experienced with this, please take a look at my code.
import React, { Component } from 'react';
import { Text, View, ScrollView, TextInput, } from 'react-native';
import { List, ListItem } from 'react-native-elements';
import { users } from '../config/data';
class Feed extends Component { constructor(props){
super(props);
this.state = {
user:'',
} } onLearnMore = (user) => {
this.props.navigation.navigate('Details', { ...user }); };
filterSearch(text){
const newData = users.filter((item)=>{
const itemData = item.name.first.toUpperCase()
const textData = text.toUpperCase()
return itemData.indexOf(textData)>-1
})
this.setState({
text:text
}) }
render() {
return (
<ScrollView>
<TextInput
onChangeText={(text) => this.filterSearch(text)}
value={this.state.text}
/>
<List>
{users.map((user) => (
<ListItem
key={user.login.username}
roundAvatar
avatar={{ uri: user.picture.thumbnail }}
title={`${user.name.first.toUpperCase()} ${user.name.last.toUpperCase()}`}
subtitle={user.email}
onPress={() => this.onLearnMore(user)}
/>
))}
</List>
</ScrollView>
); } }
export default Feed;
i have been surfing the internet but i found that most of it discuss about listview instead of list item from react-native-elements, help me!
You were almost correct. You successfully filter your users but then render the same not filtered users in your list. To change this easily you can use component state.
Example
import React, { Component } from 'react';
import { Text, View, ScrollView, TextInput, } from 'react-native';
import { List, ListItem } from 'react-native-elements';
import { users } from '../config/data';
class Feed extends Component {
constructor(props){
super(props);
this.state = {
user:'',
users: users // we are setting the initial state with the data you import
}
}
onLearnMore = (user) => {
this.props.navigation.navigate('Details', { ...user });
};
filterSearch(text){
const newData = users.filter((item)=>{
const itemData = item.name.first.toUpperCase()
const textData = text.toUpperCase()
return itemData.indexOf(textData)>-1
});
this.setState({
text:text,
users: newData // after filter we are setting users to new array
});
}
render() {
// rather than mapping users loaded from data file we are using state value
return (
<ScrollView>
<TextInput
onChangeText={(text) => this.filterSearch(text)}
value={this.state.text}
/>
<List>
{this.state.users.map((user) => (
<ListItem
key={user.login.username}
roundAvatar
avatar={{ uri: user.picture.thumbnail }}
title={`${user.name.first.toUpperCase()} ${user.name.last.toUpperCase()}`}
subtitle={user.email}
onPress={() => this.onLearnMore(user)}
/>
))}
</List>
</ScrollView>
); } }
export default Feed;
why do i keep answering my own answer
i am so sorry to this forum that i waste some space
but i thought posting this answer will help some of you especially a beginner like me
import React, {Component} from 'react';
import { StyleSheet, Text, View, ListView, TouchableHighlight, TextInput} from 'react-native';
import { List, ListItem } from 'react-native-elements';
import { users } from '../config/data';
export default class Feed extends Component {
constructor(props){
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1,r2) => r1 !== r2})
this.state = {
dataSource: ds.cloneWithRows(users),
text:'',
}
}
onLearnMore = (rowData) => {
this.props.navigation.navigate('Details', { ...rowData });
};
renderRow(rowData){
return(
<ListItem
key={rowData.login.username}
roundAvatar
avatar={{ uri: rowData.picture.thumbnail }}
title={`${rowData.name.first.toUpperCase()} ${rowData.name.last.toUpperCase()}`}
subtitle={rowData.email}
onPress={() => this.onLearnMore(rowData)}
/>
);
}
filterSearch(text){
const newData = users.filter(function(item){
const itemData = item.email.toUpperCase()
const textData = text.toUpperCase()
return itemData.indexOf(textData) > -1
})
this.setState({
dataSource: this.state.dataSource.cloneWithRows(newData),
text: text
})
}
render() {
return (
<View style={{flex:1}}>
<TextInput
onChangeText={(text) => this.filterSearch(text)}
value={this.state.text}
/>
<ListView
enableEmptySections={true}
style={{marginHorizontal:10}}
renderRow={this.renderRow.bind(this)}
dataSource={this.state.dataSource}
/>
</View>
);
}
}
just compare the question's code and the answer code
and lastly i get the answer by reading the link below
https://react-native-training.github.io/react-native-elements/API/lists/
feel free to check it out again

ListView datasource is not updating - React-Native

I'm trying to update listview from a function, But currently it is not updating, Here is the complete source code.
Please also mention that what I am doing wrong
import React, { Component } from 'react';
import {
AppRegistry,
ListView,
TextView,
Text,
View
} from 'react-native';
const ds = new ListView.DataSource({ rowHasChanged: (row1, row2) => row1 != row2 });
var myarray = [];
export default class filesix extends Component {
constructor() {
super();
this.state = {
dataSource: ds.cloneWithRows(myarray),
};
console.log(`ds const =${myarray}`);
}
componentDidMount() {
myarray = ['11', '22'];
console.log(myarray);
this.setState = ({
datasource: this.state.dataSource.cloneWithRows(myarray),
});
this.prepareDataSource();
console.log('this componentDidMount');
}
prepareDataSource() {
myarray = ['11', '22'];
console.log(myarray);
}
renderRow(rowData) {
return <Text>{JSON.stringify(rowData)}</Text>
}
render() {
return (
<View style={{ flex: 1, borderWidth: 2 }}>
<ListView
enableEmptySections={true}
dataSource={this.state.dataSource}
renderRow={this.renderRow}
/>
</View>
);
}
}
AppRegistry.registerComponent('filesix', () => filesix);
I already spent my whole day to update the values, but no luck, Please correct my understanding.
Your componentDidMount has a typo. It's setting state on datasource but your ListView is using dataSource.

Categories

Resources