Pass data from Android Native to React Native - android

I am building an integrated app with Android native and React native communicating with each other.
For sending data from Native to React native I tried to pass data by using initial props but it was not working and showing undefined. Then I tried to use DeviceEventEmitter which kind of worked but there was a slight problem.
EDITED :
Here's the code snippet:
class Details extends Component {
constructor(props){
super(props);
this.state={data: '', id: '278'}
}
componentDidMount(){
const eventEmitter = new NativeEventEmitter();
this.subscription = eventEmitter.addListener('customEventName',(e: Event)=>{
this.setState({id: e.key1});
console.warn(this.state.id);
});
const API_key = "APIkey"
console.warn(this.state.id);
const URL = "https://api.themoviedb.org/3/movie/" + this.state.id + "?api_key=" + API_key + "&language=en-USs"
return fetch(URL, {
method: 'GET'
})
.then((response) => response.json())
.then((responseJson) => {
this.setState({
data: responseJson,
},
function(){
});
})
.catch((error) =>{
console.error(error);
});
}
componentWillUnmount(){
this.subscription.remove();
}
render() {
return(
/*something*/
);
}
}
The value of id is being successfully sent from native component to React native component.
Problem: The console.warn() inside addlistener() is showing after the console.warn() which is below declaring the API_key, and hence the this.state.id is not being updated.
Please any help will be appreciated.

your event register should be something as mentioned below, as you are registering the event so the scope of this should be event handler specific so if you want to access the parent scope you need to use the arrow function like mentioned below.
DeviceEventEmitter.addListener('customEventName',(e: Event)=> {
this.id = e.key1
console.warn("inside event emitter", this.id);
});

If you are successfully getting the event then I think this is just a React problem.
It looks like you want to fetch after you have successfully got the ID, but you're trying to fetch straight away in componentDidMount.
As fetch is a side effect you probably want to use componentDidUpdate like so:
import { NativeEventEmitter } from 'react-native'
constructor(props){
super(props);
this.state={
data: '',
id: ''
}
}
componentDidMount(){
const eventEmitter = new NativeEventEmitter();
this.subscription = eventEmitter.addListener('customEventName',(e: Event)=>{
this.setState({id: e.key1});
console.warn(this.state.id);
});
}
componentDidUpdate() {
const { id } = this.state
if (id) {
const URL = "https://api.themoviedb.org/3/movie/" + this.state.id + "?api_key=" + API_key + "&language=en-USs"
return fetch(URL, {
method: 'GET'
})
// ... etc
}
}
// ...etc
Note that id starts out as empty.

Related

Uploading image to firebase in react native: undefined is not a function

As the title says, I'm trying to upload Image to firebase in react native. I'm using react-native-image-picker and firebase modules for that. My code goes as: (Only including the "main" parts for clarity)
import ImagePicker from 'react-native-image-picker';
...
//called on pressing a button
onChooseImagePress = async () => {
let result = await ImagePicker.open({ //error occurs here
takePhoto: true,
useLastPhoto: true,
chooseFromLibrary: true
});
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('images').child("userName/" + imageName);
return ref.put(blob);
}
....
Issue:
I am getting this error: undefined is not a function. Here's a screenshot of the same:
I'm not sure what it even means, since ImagePicker has an open function. Please note that I have provided the desired permissions. So it is not an issue due to that. Please help me resolve this. Thanks...
Are you using React-native ImagePicker? There is no open in the API document.
API Reference of react-native-image-picker
This is the default example of getting the value of the selected image you want.
import ImagePicker from 'react-native-image-picker';
// More info on all the options is below in the API Reference... just some common use cases shown here
const options = {
title: 'Select Avatar',
customButtons: [{ name: 'fb', title: 'Choose Photo from Facebook' }],
storageOptions: {
skipBackup: true,
path: 'images',
},
};
/**
* The first arg is the options object for customization (it can also be null or omitted for default options),
* The second arg is the callback which sends object: response (more info in the API Reference)
*/
ImagePicker.launchImageLibrary(options, (response) => {
console.log('Response = ', response);
if (response.didCancel) {
console.log('User cancelled image picker');
} else if (response.error) {
console.log('ImagePicker Error: ', response.error);
} else if (response.customButton) {
console.log('User tapped custom button: ', response.customButton);
} else {
const source = { uri: response.uri };
// You can also display the image using data:
// const source = { uri: 'data:image/jpeg;base64,' + response.data };
this.setState({
avatarSource: source,
});
}
});

React Native, AWS, Error only on Android "TypeError: Cannot read Property 'handle' of null"

I ran into a bug whenever I run my React Native app on an Android device (physical and emulator). Yet, no problem at all on iOS. These functions are supposed to scan the database table for user handles and return an object if the handle already exists.
This is what the error looks like:
TypeError: Cannot read property 'handle' of null
at exports.handler (/var/task/index.js:7:36)
I'm using React Native, AWS Lambda, and EXPO.
This code lives within dbfunctions.js on the front end.
export async function scanHandles(){
return new Promise((resolve, reject) => {
let { auth } = store.getState()
let reqBody = {
userId: auth.user.username,
handle: auth.handle_update,
}
let path = '/u/scan-handle'
let myInit = {
headers: { 'Content-Type': 'application/json' },
body: reqBody,
}
console.log('myInit', myInit)
console.log('handle', auth.handle_update)
API.get(apiName, path, myInit)
.then((resp) => {
// if false, then handle does not exist
// if true, then handle already exists
resolve(resp)
})
.catch((error) => {
console.warn('Scan Handle', error)
reject(error)
})
})
}
Console logging auth.handle_update does print out the expected string. myInit also prints out the expected object.
On the back end, I'm using this for my scan:
const AWS = require("aws-sdk");
const docClient = new AWS.DynamoDB.DocumentClient({ region: "us-west-1" });
exports.handler = (event, context, callback) => {
let e = JSON.parse(event.body);
var params = {
TableName: event.stageVariables.user,
FilterExpression: "handle = :handle",
ExpressionAttributeValues: { ":handle": e.handle }
};
docClient.scan(params, function(err, data) {
if (err) {
console.log("ERROR:", err);
let response = {
statusCode: err.statusCode,
headers: {},
body: JSON.stringify(err)
};
callback(response);
}
if (data.Count >= 1) {
// if user name exists
// call back handle exists response
let handleExistsResponse = {
statusCode: 200,
body: JSON.stringify({ Success: true })
};
callback(null, handleExistsResponse);
} else {
let response = {
statusCode: 200,
body: JSON.stringify({ Success: false })
};
callback(null, response);
}
});
};
Any idea as to why this would work on iOS and not Android?
EDIT:
Upon further testing, let e = JSON.parse(event.body) is returning null. So I console logged event and got a big ol object. Within this object, I found body and it's still null. So the body object isn't being passed it properly. Still confused about it working on iOS and not Android.
Did it!
Okay so API.get doesn't like body's being passed in. Instead, it wants a query parameter. So the lambda params looks like:
var params = {
TableName: event.stageVariables.user,
FilterExpression: "handle = :handle",
ExpressionAttributeValues: {
":handle": event["queryStringParameters"]["handle"]
}
};
And the front end function is:
export async function scanHandles(){
return new Promise((resolve, reject) => {
let { auth } = store.getState()
let handle = auth.handle_update
let path = `/u/scan-handle?handle=${handle}`
let myInit = {
headers: { 'Content-Type': 'application/json' },
}
API.get(apiName, path, myInit)
.then((resp) => {
// if false, then handle does not exist
// if true, then handle already exists
resolve(resp)
})
.catch((error) => {
console.warn('Scan Handle', error)
reject(error)
})
})
}
Works on both iOS and Android. Wonder why it wasn't working before?

React Native - Share Method Cancel Event

i am using React Native Share Method (https://facebook.github.io/react-native/docs/share.html) to share content on Android.
As per documentation, we can use it in following ways:
Share.share({
message: 'React Native | A framework for building native apps using React'
})
.then((result) => {
console.log(result.action) // returns sharedAction
})
.catch((error) => {
console.log(error)
})
So when we call Share method, we will get result in then and popup window is appeared which contains apps list with which we can share the message content.
Issue:
Popup window also contains a cancel button, so when user click on it, window get closed, but there is no method available/mentioned to capture it, in docs.
Is there any way to capture the cancel event, as i want to perform some action when user clicks on it.
Thanks in adv. :)
There is option in share dismissedAction but unfortunately this is IOS only . You can use react-native-share with prop "failOnCancel" to true and it will work on both android and ios
Sample Code
import React, { Component } from "react";
import { Button } from "react-native";
import Share from "react-native-share";
export default class ShareExample extends Component {
onShare = async () => {
const shareOptions = {
title: "Share file",
social: Share.Social.EMAIL,
failOnCancel: true
};
try {
const ShareResponse = await Share.open(shareOptions);
//setResult(JSON.stringify(ShareResponse, null, 2));
} catch (error) {
console.log("Error =>", error);
}
};
render() {
return <Button onPress={this.onShare} title="Share" />;
}
}
App Preview
This might help
import React, {Component} from 'react';
import {Share, Button} from 'react-native';
class ShareExample extends Component {
onShare = async () => {
try {
const result = await Share.share({
message:
'React Native | A framework for building native apps using React',
});
if (result.action === Share.sharedAction) {
if (result.activityType) {
// shared with activity type of result.activityType
} else {
// shared
}
} else if (result.action === Share.dismissedAction) {
// dismissed
}
} catch (error) {
alert(error.message);
}
};
render() {
return <Button onPress={this.onShare} title="Share" />;
}
}

TypeError: Requested keys of a value that is not an object. React-native

guys! I first making app with and have some problem with response values! When i click GO, i sent request and get response from it. Im looking in the console, i have 20 items from response on 'london' locate, so it works, but doesnt parse my json to key-valuse! Help me, please, guys!
Using latest React-native + android virtual emulator 7.1.1
Here is my code from SearchPage.js
function urlForQueryAndPage(key, value, pageNumber) {
var data = {
country: 'uk',
pretty: '1',
encoding: 'json',
listing_type: 'buy',
action: 'search_listings',
page: pageNumber
};
data[key] = value;
var querystring = Object.keys(data)
.map(key => key + '=' + encodeURIComponent(data[key]))
.join('&');
return 'http://api.nestoria.co.uk/api?' + querystring;
};
export default class SearchPage extends Component {
constructor(props) {
super(props);
this.state = {
searchString: 'london',
isLoading: false,
message: ''
};
}
onSearchTextChanged(event) {
this.setState({ searchString: event.nativeEvent.text });
}
onSearchPressed() {
var query = urlForQueryAndPage('place_name', this.state.searchString, 1);
this._executeQuery(query);
}
_executeQuery(query) {
this.setState({ isLoading: true });
console.log(query);
fetch(query)
.then(response => response.json())
.then(json => this._handleResponse(json.response))
.catch(error =>
this.setState({
isLoading: false,
message: 'Something bad happened ' + error
}));
}
_handleResponse(response) {
this.setState({ isLoading: false , message: '' });
if (response.application_response_code.substr(0, 1) === '1') {
console.log('Properties found: ' + response.listings.length);
this.props.navigator.push({
id: 'SearchResults',
name: 'SearchResults',
passProps: {listings: response.listings}
});
console.log(passProps);
} else {
this.setState({ message: 'Location not recognized; please try again.'});
}
}
And here is my code from SearchResults.js
export default class SearchResults extends Component {
constructor(props) {
super(props);
var dataSource = new ListView.DataSource(
{rowHasChanged: (r1, r2) => r1.lister_url !== r2.lister_url});
this.state = {
dataSource: dataSource.cloneWithRows(this.props.listings)
};
}
renderRow(rowData, sectionID, rowID) {
return (
<TouchableHighlight
underlayColor='#dddddd'>
<View>
<Text>{rowData.title}</Text>
</View>
</TouchableHighlight>
);
}
render() {
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow.bind(this)}/>
);
}
}
Look at the error text ( in red pointer)
End here it is response from console. Look at the items count!So response works!
Two things:
1-Try to put the .then right after response.json() like so:
fetch(query)
.then((response) => {
response.json()
.then(json => this._handleResponse(json.response))})
.catch(error =>
Update: Actually, changing the order of the .then is not necessary, it should work either way.
2-I believe that when you console.log(passProps), passProps is not defined properly, therefore, console.log(passProps) won't print anything (Correct me if I am wrong). You can try this:
var passProps = {listings: response.listings};
this.props.navigator.push({
id: 'SearchResults',
name: 'SearchResults',
passProps: passProps
});
console.log(passProps);
I see that you are using this.props.listings inside your SearchResults component after passing listings as props using the navigator. The only way for you to be able to do this.props.listings directly (instead of this.props.passProps.listings) is if you have something like this in your renderScene method of your navigator:
if (routeId === 'SearchResults') {
return (<SearchResults {...route.passProps} navigator={navigator} />);
}
or
if (route.id === 'SearchResults') {
return (<SearchResults listings={route.passProps.listings} navigator={navigator} />);
}
To make sure you are passing the props correctly to the SearchResults component, do a console.log in your render method of SearchResults
render() {
console.log("SearchResults props: ", this.props);
return (
<ListView
I believe that if you get the fetch output right (as an object), the problem could be the way you wrote your ListView and/or dataSource.
Other than that, there is not enough information (Such as the expected json response, and what you actually got) to tell where the problem lies.
Cheers

Uncaught TypeError: Cannot call method 'post' of undefined at file:///android_asset/www/build/js/app.bundle.js Ionic 2 [duplicate]

This question already has an answer here:
Variable not updating with fetch response data in Angular 2 beta
(1 answer)
Closed 6 years ago.
I try to explain in English, but I don't speak it.
I'm working in a Ionic 2. I try to do a http request with post method and I am emulate in SDK Android emulator and I can see in the logcat:
Cannot call method 'post' of undefined at
file:///android_asset/www/build/js/app.bundle.js:2265
But I review and don't see anything, I rewrite my clientId and ClientSecret to can post here. I put a trace console.log(this.http) in the login function and this attribute is undefined, althought is inject in the class' constructor.
My code:
import {Page, Platform} from 'ionic-angular';
import {Http, Headers, HTTP_PROVIDERS} from 'angular2/http';
#Page({
templateUrl: 'build/pages/home/home.html',
providers: [ HTTP_PROVIDERS ]
})
export class HomePage {
static get parameters() {
return [[Platform],[Http]];
}
constructor(platform, http) {
this.platform = platform;
this.http = http;
this.clientId = "clientId";
this.clientSecret = "clientSecret";
}
login() {
this.platform.ready().then(() => {
this.googleLogin().then((success) => {
alert(success.access_token);
}, (error) => {
alert(error);
});
});
}
googleLogin() {
return new Promise(function(resolve, reject) {
var browserRef = window.cordova.InAppBrowser.open("https://accounts.google.com/o/oauth2/auth?client_id=" + "clientId" + "&redirect_uri=http://localhost/callback&scope=email%20profile&approval_prompt=force&response_type=code&access_type=offline", "_blank", "location=no,clearsessioncache=yes,clearcache=yes");
browserRef.addEventListener("loadstart", (event) => {
if ((event.url).indexOf("http://localhost/callback") === 0) {
var headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
var parameters = "client_id=" + "clientId" + "&client_secret=" + "clientSecret" + "&redirect_uri=http://localhost/callback" + "&grant_type=authorization_code" + "&code=" + requestToken
var requestToken = (event.url).split("code=")[1];
this.http.post("https://accounts.google.com/o/oauth2/token", parameters, { header:headers })
.subscribe( data => { resolve(data); },
error => { reject("Problem authenticating with Google"); }
);
browserRef.removeEventListener("exit", (event) => {});
browserRef.close();
}
});
browserRef.addEventListener("exit", function(event) {
reject("The Google sign in flow was canceled");
});
});
}
}
The code tries to authenticate with Google OAuth2, althought the error seems to be in the attributes in the constructor(http, clientId, clientSecret) there are not defined when the login function is called. I don't know what's wrong!
It might have something to do with the scoping of 'this', depending on what calls the googleLogin function.
Try using an arrow function:
googleLogin = () => {
...
}
It's because you don't use an arrow function when defining your promise. So the this keyword doesn't correspond to the instance of the component itself. With arrow functions, you can use the lexical this that will correspond to the component instance.
googleLogin() {
return new Promise((resolve, reject) => {
(...)
});
}
instead of
googleLogin() {
return new Promise(function(resolve, reject) {
(...)
});
}
See this link for more hints about the lexical this of arrow functions:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions.

Categories

Resources