I'm trying to pass some data to a modal screen with react-native-navigation pacakage 1.1.65 (https://github.com/wix/react-native-navigation)
I have two cases :
First one
export default class SearchTab extends Component {
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dicoDataSource: ds.cloneWithRows(realm.objects('User')),
searchText:'',
data:[]
}
}
onPressButton() {
var resultData = this.state.data;
if(resultData.length > 0){
console.log("RESULTDATA", resultData);
this.props.navigator.showModal({
title: "Modal",
screen: "App.SearchResult",
passProps: {
result: resultData,
}
});
}
}
When I clicked the button, it fires me this error :
'Error calling RCTEventEmiter.receiveTouches'
The log "RESULTDATA" is something like that with one or several items :
RESULTDATA', { '0':
{ id: 1,
name: 'Leanne Graham',
username: 'Bret',
email: 'Sincere#april.biz'
} }
Second one
export default class SearchTab extends Component {
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dicoDataSource: ds.cloneWithRows(realm.objects('User')),
searchText:'',
data:[]
}
}
onPressButton() {
var resultData = this.state.data;
if(resultData.length > 0){
console.log("RESULTDATA", resultData);
this.props.navigator.showModal({
title: "Modal",
screen: "App.SearchResult",
passProps: {
result: resultData.name, <== HERE THE ONLY DIFFERENCE
}
});
}
}
With this code, the modal screen shows up but when I log this.props.result it shows undefined.
componentDidMount(){
console.log("PROPS", this.props.result);
}
I would like to use this data to make a ListView in the modal screen which works fine.
No idea what to do with that. I already tested separately some UI elements and with different combinations like described above.
And I want to have the first one to work.
Any suggestion would be highly appreciated.
EDIT
Nobody ?
EDIT 2
Here my SearchResult class:
import React, {Component} from 'react';
import {
TextInput,
View,
TouchableOpacity,
StyleSheet,
TouchableHighlight,
Text,
Button
} from 'react-native';
import realm from '../realmDB/realm';
import { ListView } from 'realm/react-native';
import {Navigation} from 'react-native-navigation';
import EStyleSheet from 'react-native-extended-stylesheet';
export default class SearchResult extends Component {
static navigatorStyle = {
leftButtons: [{
title: 'Close',
id: 'close'
}]
};
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
resultDataSource: ds.cloneWithRows(this.props.result),
searchText:'',
data:[]
}
}
renderRow(rowData, sectionId, rowId, highlightRow){
return(
<View style={styles.row}>
<Text style={styles.rowText}>{rowData.username}</Text>
</View>
)
}
render() {
return (
<View style={styles.container}>
<TextInput style = {styles.searchText}
placeholder="Type your research"
autoCorrect={true}
returnKeyLabel="search"
underlineColorAndroid="black"
placeholderTextColor="black"
value = {this.state.searchText}
onChange={this.setSearchText.bind(this)}
/>
<TouchableOpacity onPress = {() => this.onPressButton(this.state.searchText)}>
<Text style={styles.button}>SEARCH</Text>
</TouchableOpacity>
<ListView
navigator={this.props.navigator}
enableEmptySections={true}
dataSource={this.state.resultDataSource}
renderRow={this.renderRow.bind(this)}
renderSeparator={(sectionId, rowId) => <View key={rowId} style={styles.separator} />}
/>
</View>
);
I also open an issue here: https://github.com/wix/react-native-navigation/issues/1249
Make sure that you are passing the 'result' props from the 'App.SearchResult' to the 'SearchTab' component when you are rendering it in the screen component.
Ok, it was not a context losing problem. It was about the data structure I used. I had to make nested objects in order to pass the data.
I was trying to pass a wrong format/structure of data that react-native-navigation package did not allow. Only an object can be passed
Related
Is there any way to abort a fetch request on react-native app ?
class MyComponent extends React.Component {
state = { data: null };
componentDidMount = () =>
fetch('http://www.example.com')
.then(data => this.setState({ data }))
.catch(error => {
throw error;
});
cancelRequest = () => {
//???
};
render = () => <div>{this.state.data ? this.state.data : 'loading'}</div>;
}
i tried the abort function from AbortController class but it's not working !!
...
abortController = new window.AbortController();
cancelRequest = () => this.abortController.abort();
componentDidMount = () =>
fetch('http://www.example.com', { signal: this.abortController.signal })
....
Any help please !
You don't need any polyfill anymore for abort a request in React Native 0.60 changelog
Here is a quick example from the doc of react-native:
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* #format
* #flow
*/
'use strict';
const React = require('react');
const {Alert, Button, View} = require('react-native');
class XHRExampleAbortController extends React.Component<{}, {}> {
_timeout: any;
_submit(abortDelay) {
clearTimeout(this._timeout);
// eslint-disable-next-line no-undef
const abortController = new AbortController();
fetch('https://facebook.github.io/react-native/', {
signal: abortController.signal,
})
.then(res => res.text())
.then(res => Alert.alert(res))
.catch(err => Alert.alert(err.message));
this._timeout = setTimeout(() => {
abortController.abort();
}, abortDelay);
}
componentWillUnmount() {
clearTimeout(this._timeout);
}
render() {
return (
<View>
<Button
title="Abort before response"
onPress={() => {
this._submit(0);
}}
/>
<Button
title="Abort after response"
onPress={() => {
this._submit(5000);
}}
/>
</View>
);
}
}
module.exports = XHRExampleAbortController;
I've written quite a bit actually about this subject.
You can also find the first issue about the OLD lack of AbortController in React Native opened by me here
The support landed in RN 0.60.0 and you can find on my blog an article about this and another one that will give you a simple code to get you started on making abortable requests (and more) in React Native too. It also implements a little polyfill for non supporting envs (RN < 0.60 for example).
You can Actually achieve this by installing this polyfill abortcontroller-polyfill
Here is a quick example of cancelling requests:
import React from 'react';
import { Button, View, Text } from 'react-native';
import 'abortcontroller-polyfill';
export default class HomeScreen extends React.Component {
state = { todos: [] };
controller = new AbortController();
doStuff = () => {
fetch('https://jsonplaceholder.typicode.com/todos',{
signal: this.controller.signal
})
.then(res => res.json())
.then(todos => {
alert('done');
this.setState({ todos })
})
.catch(e => alert(e.message));
alert('calling cancel');
this.controller.abort()
}
render(){
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
<Button title="Do stuff" onPress={() => { this.doStuff(); }} />
</View>
)
}
}
So basically in this example, once you click the 'doStuff' button, the request is immediately cancelled and you never get the 'done' alert. To be sure, it works, try and comment out these lines and click the button again:
alert('calling cancel');
this.controller.abort()
This time you will get the 'done' alert.
This is a simple example of hoe you can cancel a request using fetch in react native, feel free to adopt this to your own use case.
Here is a link to a demo on snackexpo https://snack.expo.io/#mazinoukah/fetch-cancel-request
hope it helps :)
the best solution is using rxjs observables + axios/fetch instead of promises, abort a request => unsubscribe an observable :
import Axios from "axios";
import {
Observable
} from "rxjs";
export default class HomeScreen extends React.Component {
subs = null;
doStuff = () => {
let observable$ = Observable.create(observer => {
Axios.get('https://jsonplaceholder.typicode.com/todos', {}, {})
.then(response => {
observer.next(response.data);
observer.complete();
})
});
this.subs = observable$.subscribe({
next: data => console.log('[data] => ', data),
complete: data => console.log('[complete]'),
});
}
cancel = () =>
if (this.subs) this.subs.unsubscribe()
componentWillUnmount() {
if (this.subs) this.subs.unsubscribe();
}
}
That is it :)
I'm trying to connect to a device using BLE connection in react-Native on Android Device.
I need to connect to a device with a with a specific name: for example "deviceName".
I'm using react-native-ble-plx.
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
TouchableOpacity,
ScrollView,
FlatList,
TextInput,
Platform,
Alert
} from 'react-native';
import { BleManager } from 'react-native-ble-plx';
export default class Main extends Component {
constructor(props) {
super(props);
this.state={
scaning:false,
isConnected:false,
text:'',
writeData:'',
receiveData:'',
readData:'',
bleManager: new BleManager(),
data:[],
isMonitoring:false,
}
this.bluetoothReceiveData = [];
this.deviceMap = new Map();
}
scan() {
if(!this.state.scaning) {
this.setState({scaning:true});
this.deviceMap.clear();
const { bleManager } = this.state;
bleManager.startDeviceScan(null, null, async (error, device) => {
console.log("scanning bluetooth...")
if (device.name === "Proximity") {
bleManager.connectToDevice(device.id, {
autoconnect: true,
timeout: BLUETOOTH_TIMEOUT,
isConnected: true
})
// ............
}
})
}
}
disconnect(){
bleManager.disconnect()
.then(res=>{
this.setState({data:[...this.deviceMap.values()],isConnected:false});
})
.catch(err=>{
this.setState({data:[...this.deviceMap.values()],isConnected:false});
})
}
render(){
return(
<View>
<TouchableOpacity
activeOpacity={0.7}
style={[styles.buttonView,{marginHorizontal:10,height:40,alignItems:'center'}]}
onPress={this.state.isConnected?this.disconnect.bind(this):this.scan.bind(this)}>
<Text style={styles.buttonText}>{this.state.scaning?'Search':this.state.isConnected?'Disconnect Bluetooth':'Search Bluetooth'}</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor:'white',
marginTop:Platform.OS == 'ios'?20:0,
},
item:{
flexDirection:'column',
borderColor:'rgb(235,235,235)',
borderStyle:'solid',
borderBottomWidth:StyleSheet.hairlineWidth,
paddingLeft:10,
paddingVertical:8,
},
buttonView:{
height:30,
backgroundColor:'rgb(33, 150, 243)',
paddingHorizontal:10,
borderRadius:5,
justifyContent:"center",
alignItems:'center',
alignItems:'flex-start',
marginTop:10
},
buttonText:{
color:"white",
fontSize:12,
},
content:{
marginTop:5,
marginBottom:15,
},
textInput:{
paddingLeft:5,
paddingRight:5,
backgroundColor:'white',
height:50,
fontSize:16,
flex:1,
},
})
At the moment I receive this error: "undefined is not an object (evaluating 'b.default.startDeviceScan').
How can I fix this error? and do you think the code can work to connect directly to a device? thank you
You are exporting BleManager wrong. You have to put it between braces like this:
import { BleManager } from 'react-native-ble-plx';
You are using BleManager wrong too. You have to instantiate it in some place, I use to use it in the state, to ensure that I have only 1 BleManager and make a new Object of BleManager like this:
constructor {
....
this.state = {
....
bleManager: new BleManager(),
....
};
And then use this.state.bleManager instead of BleManager you was using like this:
const { bleManager } = this.state;
bleManager.startDeviceScan(...)
hey guys could someone please look over this. I cant find where its comming from i've been looking on this page the last days and i cant find it
the Problem is everytime when i`m clicking on one of the names in the list the navigation and the Transver of the Values to "global.dialed" works perfektly but im always getting this warning and the app seems to perform a little slower after that (but the slower performance is very minor and probably just an illusion)
Full error:
Warning: Can't call setState (or forceUpdate) on an unmounted component.
This is a no-op, but it indicates a memory leak in your application. To fix,
cancel all subscriptions and asynchronous tasks in the componentWillUnmount
method.
stacktrace is pointing to line 25
RecentCalls.js:
import React, { Component } from "react";
import { Text, View, ListView, TouchableHighlight } from "react-native";
import styles from "./../Styles/styles";
import global from "./../Components/global";
export default class RecentCalls extends Component {
constructor() {
super();
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2
});
this.state = {
userDataSource: ds
};
}
componentDidMount() {
this.fetchUsers();
}
fetchUsers() {
fetch("https://jsonplaceholder.typicode.com/users")
.then(response => response.json())
.then(response => {
this.setState({
userDataSource: this.state.userDataSource.cloneWithRows(response)
});
});
}
onPress(user) {
this.props.navigation.navigate("Home3", {});
global.dialed = user.phone;
}
renderRow(user, sectionId, rowId, highlightRow) {
return (
<TouchableHighlight
onPress={() => {
this.onPress(user);
}}
>
<View style={[styles.row]}>
<Text style={[styles.rowText]}>
{user.name}: {user.phone}
</Text>
</View>
</TouchableHighlight>
);
}
render() {
return (
<View style={styles.padding2}>
<ListView
dataSource={this.state.userDataSource}
renderRow={this.renderRow.bind(this)}
/>
</View>
);
}
}
Thank you very much for your Time and effort in advance :)
edit 1
according to milkersarac I Tried (See below) but it made no difference
edited Constructor to:
constructor() {
super();
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2
});
this.state = {
userDataSource: ds
};
this.fetchUsers = this.fetchUsers.bind(this);
}
You need to .bind(this) the function that you are updating component state in your components' constructor. Namely try adding this.fetchUsers = this.fetchUsers.bind(this) as the last line to your ctor.
I'm having serious issues with the RN Picker Item, whenever I try to load the picker Items I get the following error.
undefined is not an object (evaluating 'this.inputProps.value')
Here us the screenshot.
This is my code - Component - Basic
import React, { Component } from 'react';
import { Picker } from 'react-native';
export default class Basic extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
var options = this.props.list.map((item, key) => {
return <Picker.Item label={item} value={item} key={key} /> ;
});
return (
<Picker mode="dropdown" selectedValue={this.props.selected} supportedOrientations={['portrait','landscape']} {...this.props}>
{ this.props.default && <Picker label={this.props.default} value=""/> }
{ options }
</Picker>
);
}
}
File - Dynamic OptionSet
This will use the Basic component to display the Picker.
class DynamicOptionSets extends Component {
constructor(props) {
super(props);
this.state = {};
this.ucfirst = this.ucfirst.bind(this);
this._renderMain = this._renderMain.bind(this);
this._renderSpinner = this._renderSpinner.bind(this);
}
componentWillMount() {
InteractionManager.runAfterInteractions(() => {
this.props["get"+this.ucfirst(this.props.option)]();
});
}
ucfirst(string)
{
return string.charAt(0).toUpperCase() + string.slice(1);
}
render() {
return (
<View>
{this._renderSpinner()}
{this._renderMain()}
</View>
);
}
_renderMain(){
if(!this.props[this.props.option]['data']){
return null;
}
return (
<Basic list={this.props[this.props.option]['data']} { ...this.props }/>
)
}
_renderSpinner(){...}
}
const mapDispatchToProps = (dispatch, ownProps) => {
var {getCountries, getStates,
getDepartments, getBranches,
getBusinessSectors, getGenPostingGroup,
getCustPostingGroup, getVatPostingGroup,
getPricelist, getSalesPersons
} = ActionCreators;
return bindActionCreators({
getCountries, getStates,
getDepartments, getBranches,
getBusinessSectors, getGenPostingGroup,
getCustPostingGroup, getVatPostingGroup,
getPricelist, getSalesPersons
}, dispatch);
}
const mapStateToProps = (state) => {
var {
countries, countriesUpdate,
states, statesUpdate,
departments, departmentsUpdate,
branches, branchesUpdate,
businessSectors, businessSectorsUpdate,
genPostingGroup, genPostingGroupUpdate,
ccustPostingGroup, ccustPostingGroupUpdate,
vatPostingGroup, vatPostingGroupUpdate,
pricelist, pricelistUpdate,
salesPersons, salesPersonsUpdate,
} = state;
return {
countries, countriesUpdate,
states, statesUpdate,
departments, departmentsUpdate,
branches, branchesUpdate,
businessSectors, businessSectorsUpdate,
genPostingGroup, genPostingGroupUpdate,
ccustPostingGroup, ccustPostingGroupUpdate,
vatPostingGroup, vatPostingGroupUpdate,
pricelist, pricelistUpdate,
salesPersons, salesPersonsUpdate,
}
}
export default connect(mapStateToProps, mapDispatchToProps)(DynamicOptionSets);
So now I can call the dynamic option set like a regular picker component only and specify the data group (option)
<DynamicOptionSets option="salesPersons" mode="dropdown" onValueChange={this._updateValue.bind(this, 'salesperson')} selectedValue={this.state.form_data.salesperson} />
I don't understand why this is happening as this is the exact way I render Pickers dynamically in RN. I have gone through the doc and followed the instructions as specified.
NB: I'm dynamically loading the picker so it's inside a component I'm calling whenever I need to, display a picker that should explain the {... this.props} on the picker component.
You have a basic mistake in your code.
render() {
var options = this.props.list.map((item, key) => {
return <Picker.Item label={item} value={item} key={key} /> ;
});
return (
<Picker mode="dropdown" selected={this.props.selected} supportedOrientations={['portrait','landscape']}>
{/*_________________^^^^^^^^____ You should place `selectedValue` here instead */}
{ this.props.default && <Picker.Item label={this.props.default} value=""/> }
{ options }
</Picker>
);
}
I am using SQLite as the device's database. What I am trying to basically achieve is this:
1- Give a user the ability to star his favorite "data"
2- Once the data gets saved in the db, retrieve it inside another page and insert them into a listView for the user to see at any time.
But no matter how much I try, I am always getting the same error.
Cannot read property of undefined.
The code:
import React, { Component } from 'react'
import {
View,
Text,
ListView
} from 'react-native'
var SQLite = require('react-native-sqlite-storage')
var db = SQLite.openDatabase({ name: "RHPC.db", location: "default"})
var obj;
class Schedules extends Component {
constructor(props) {
super(props)
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2
});
this.state = {
datasource: []
}
db.transaction((tx) => {
tx.executeSql("SELECT * FROM schedules", [], (tx, res) => {
let len = res.rows.length;
if(len > 0) {
for(let i = 0; i < len; i++) {
var obj = [{id: res.rows.item(i)["id"], title: res.rows.item(i)["title"]}]
}
this.setState({
datasource: obj
})
} else {
console.log("empty")
}
})
}, (err) => {
console.log("error: " + JSON.stringify(err))
})
}
_renderRow(rowData) {
return(
<View>
<Text key={rowData.id}>
{rowData.title}
</Text>
</View>
)
}
render() {
console.log(this.state.datasource);
return(
<View style={{marginTop: 150}}>
<ListView
dataSource={this.state.datasource}
renderRow={this._renderRow.bind(this)}
/>
</View>
);
}
}
const styles = {
}
export default Schedules;
When I try to console.log the dataSource state:
0: Object
id: 2
title: "Session 1: Transition from Humanitarian Assistance to Rebuilding Health & Health Systems."
So in other words it looks like it's working but not 100%? Because I do have two rows inside that table and it's only retrieving the last one. Is this the cause of the undefined issue?
You use ListView in a wrong way, you create new dataSource in constructor (ds) and not assign it anywhere, checkout example in documentation: https://facebook.github.io/react-native/docs/listview.html
It should be:
constructor(props) {
super(props)
this.state = {
dataSource: new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}),
}
}
And in setState make something like this:
this.setState({
datasource: this.state.dataSource.cloneWithRows(obj)
})
Edit:
And in your for loop you should have:
var obj = [];
for(let i = 0; i < len; i++) {
obj.push({id: res.rows.item(i)["id"], title: res.rows.item(i)["title"]});
}