I am attempting to use the 'react-native-document-picker' module in React Native to build image file 'picking' functionality.
import React, { Component } from "react";
import { Button, Dimensions, Image, Platform, StyleSheet, Text, TextInput, TouchableOpacity, View } from "react-native";
import DocumentPicker from 'react-native-document-picker';
export default class Images extends Component {
constructor(props) {
super(props);
this.state = {
photo: null
};
}
//*****FUNCTIONS
ChoosePhoto = async () => {
try {
const res = await DocumentPicker.pick({ type: [DocumentPicker.types.images], });
//Provide the type of file you want user to pick
console.log('File Selection Response: ' + JSON.stringify(res));
console.log('File Selection Response JSON: ' + res);
console.log(
res.uri,
res.type, // mime type
res.name,
res.size
)
this.setState({ photo: res }); // Setting the state to show single file attributes
} catch (err) {
this.setState({ photo: null });
if (DocumentPicker.isCancel(err)) {
alert('Canceled'); //user canceled the document selection
} else {
alert('Selection Error: ' + JSON.stringify(err)); //Unknown Error
} //Handling any exception (if any)
} //try routine
};
//*****
render() {
return (
<View style={styles.main}>
<TouchableOpacity
style={styles.buttonStyle}
onPress={this.ChoosePhoto}
>
<Text style={styles.buttonTextStyle}>Select Image</Text>
</TouchableOpacity>
</View>
); //return
} //render
} //class 'Images' Component
I do not receive any errors upon running the code above. The problem is that all after 'picking' a file the 'console.log' statements for returned JSON values are "undefined".
In other words the command for "const res = await DocumentPicker.pick({ type: [DocumentPicker.types.images], });" seems to run OK and the initial 'console.log' (that utilizes a 'JSON.stringify' statement) returns info like this:
File Selection Response: [{"size":144672,"fileCopyUri":null,"name":"IMG-20221228-WA0002.jpg","type":"image/jpeg","uri":"content://com.android.providers.media.documents/document/image%3A2483"}]
However any other 'console.log' statement that is there (without the 'JSON.stringify' statement) just reads as "undefined"...therefore the "this.setState({ photo: res });" also seems to be setting "undefined" as the value for the "photo" variable.
Does anybody know why the JSON values are returned as "undefined" after the 'pick'? How am I to set the "this.state.photo" variable correctly using the information that is returned from the 'pick'? I thank you in advance for any advice.
react-native-document-picker return DocumentPickerResponse[] which is array even if you set
allowMultiSelection: false
so you have to use it like an array. res.uri will be undefined but res[0].uri will get you values.
according to documentation :
Breaking in v6: pick returns a Promise<Array> instead of Promise. If you were using pick, change those usages to pickSingle.
if you want to pick only one item at a time then i suggest you should use ** pickSingle** which will return you an object instead of Array.
Related
**In this my code, I got an error which says 'this.state.leader.map' is not a function.
I searched on web about my problem but none of them solved my issue.
This is my code which is below. I hope, code is clear to understand easly**
import React, { Component } from 'react';
import { View, Text, FlatList } from 'react-native';
import DOMParser from 'react-native-html-parser';
import axios from 'axios';
export default class header extends Component {
state = { leader: [] };
componentWillMount() {
fetch('url')
.then(response => {
if (response.ok) {
return response;
}else {
let error = new Error('Error ');
error.response = response;
throw error;
}
},
error => {
let errmess = new Error(error.message);
throw errmess;
})
.then(response => response.text())
.then(leaders => {
const str = leaders.substring(76);
const str2 = str.substring(0, str.length - 9);
this.setState({ leader: str2 });
})
.catch(error => {
this.setState({ errMessage: error.message });
});
}
renderall() {
return this.state.leader.map(alb => <Text>{alb.Ref}</Text>
}
render() {
console.log(this.state.leader);
return (
<View>
{this.renderall()}
</View>
);
}
}
You issue is with the fact that you are setting a string to the value of leader in your state.
If we look at your code you are taking a substring of leaders. To do this leaders must be a string. You are then saving that string to your leader value in state.
.then(leaders => {
const str = leaders.substring(76);
const str2 = str.substring(0, str.length - 9); // <- this is a string
this.setState({ leader: str2 });
})
In your state you are setting leaders in the following way
state = {
leaders: [] // <- this is an array
}
Individually doing these things are fine, but there is a disconnect between what you are doing in your fetch request and what you then want to do in your renderAll method as you cannot use .map on a string.
renderall() {
return this.state.leader.map(alb => <Text>{alb.Ref}</Text>
// .map requires this.state.leader to be an array but you
// have now changed it to a string so it won't work
}
Either you need to change what you are storing in leader in your fetch request so that it is an array. Or you need to change what is happening in your renderAll function.
XML parsing
If your XML is the same as you put in your comment
<?xml version="1.0" encoding="utf-8"?>
<string xmlns="url">[{"Ref":"IHR1900299","Unvan":"max"},{"Ref":"IHR1900298","Unvan":"max2"}] </string>
Then you could use a simple regex to capture what you want and then use JSON.parse to convert it into json.
Once you have your xml as a string, you could do this:
// I'm hardcoding it as a string, because I don't have your api to call.
let xmlString = '<?xml version="1.0" encoding="utf-8"?> <string xmlns="url">[{"Ref":"IHR1900299","Unvan":"max"},{"Ref":"IHR1900298","Unvan":"max2"}] </string>';
let jsonString = xmlString.match(/\[.*\]/);
let json = JSON.parse(jsonString);
console.log(json)
console.log(json[0].Ref)
React component usual methods (like componentWillMount or render) are automatically bound to this by React, contrary to your custom method renderAll. Add the following to your class:
constructor(props) {
super(props);
this.renderAll = this.renderAll.bind(this);
}
Useful links:
Docs on the bind keyword
Binding to this in React
Related question: React Native: bind and 'this'?
Related question: How to bind(this) when passing a function as a prop in react native?
This happen because the fetch function is asynchronous. So, all you need is to add a simple condition and passing this:
renderall() {
return this.state.leader.map(alb => <Text>{alb.Ref}</Text>
}
render() {
console.log(this.state.leader);
return (
<View>
{
(this.state.leader?this.state.leader.length>0:false)&&
this.renderall(this)
}
</View>
);
}
Your renderAll method needs to return a single JSX element in order to be correct. Additionally I reformatted the method to use arrow functions to avoid all those nasty binding issues. Also: you need to provide a unique key to each of the items.
renderall = () => {
return (
<View>
{this.state.leader.map(alb => {
return <Text key={alb.Ref}>{alb.Ref}</Text>;
})}
</View>
}
I currently learning react-native.
I am trying to stored a variable into asyncstrorage in scriptone.js and calling it in scripttwo.js
But i failed to stored the variable in scriptone.js
What i have import in scriptone.js:
import React, { Component, BackAndroid } from "react";
import { AsyncStorage } from 'AsyncStorage';
import { View, Text, StyleSheet, Button, Image, TouchableOpacity, TextInput, Alert} from "react-native";
This is part of my code in scriptone.js
class SettingScreen extends Component {
state = {
a: '70',
b: '',
c: '',
};
onPressButton = () => {
if (this.state.a == this.state.aa) {
this.setState({ b: this.state.a });
this.storeData();
}
else {
Alert("Try Again");
}
}
storeData(){
const {a} = this.state;
let mynum : a;
AsyncStorage.setItem('array',mynum)
Alert("Saved");
}
...
The error display :
"undefined is not an object(evaluating '_AsyncStorage.AsyncStorage.setItem')
May I know what the problem?
Thank you.
AsyncStorage
Usually to use AsyncStorage you first import it at the top of you file, the documentation says that you should import it as follows:
import { AsyncStorage } from 'react-native';
Which you can see here https://facebook.github.io/react-native/docs/asyncstorage
Obviously you should remove the previous import statement
import { AsyncStorage } from 'AsyncStorage';
as leaving it in will cause name conflicts.
Saving to AsyncStorage
Saving to AsyncStorage is an asynchronous task so you should use an async/await function that means you should update your storeData() function. You can see the documentation https://facebook.github.io/react-native/docs/asyncstorage for how you should do this.
storeData = async () => {
const {a} = this.state;
let mynum = a;
try {
await AsyncStorage.setItem('array', mynum)
Alert("Saved");
} catch (err) {
console.warn(err);
}
}
Setting state
Next it looks like you could be getting yourself into a race condition when you're setting the state. It takes time for setState to set the item to state. So when you call
this.setState({ b: this.state.a });
the state may not have actually been set by the time you call
this.storeData();
leading to the wrong value being stored in AsyncStorage.
To over come this there is a couple of ways you could handle this
Use setState with a callback
Pass the variable to store as a parameter to this.storeData()
Use setState with a callback
This article goes into quite some detail about using setState with a callback https://medium.learnreact.com/setstate-takes-a-callback-1f71ad5d2296 however you could refactor your onPressButton to something like this
onPressButton = () => {
if (this.state.a == this.state.aa) {
this.setState({ b: this.state.a }, async () => { await this.storeData(); });
} else {
Alert("Try Again");
}
}
This will guarantee that this.storeData() won't be run until the state has been updated.
Pass the variable to store as a parameter
This requires refactoring the storeData() function to take a parameter
storeData = async (mynum) => {
try {
await AsyncStorage.setItem('array',mynum)
Alert("Saved");
} catch (err) {
console.warn(err);
}
}
Now to use this function we have to update your onPressButton, Notice that we pass the value that we want to store to storeData that means we no longer have to access it from state inside storeData
onPressButton = async () => {
if (this.state.a == this.state.aa) {
this.setState({ b: this.state.a });
await this.storeData(this.state.a);
} else {
Alert("Try Again");
}
}
Retrieving from AsyncStorage
This is also an asynchronous task and requires an async/await. To get the string that you stored all you have to do is pass the correct key to the retrieveData function
retrieveData = async (key) => {
try {
const value = await AsyncStorage.getItem(key);
if (value !== null) {
// We have data!!
console.log(value);
// do something with the value
}
} catch (error) {
// Error retrieving data
}
}
I am Trying to implement signing up with Facebook feature for my react-native app and i am stuck at this stage that Data is successfully retrieved from the profile and I have check it on the alert screen. Here is my Code in which I call the Function of another component (FBLogin).First of all there is a Touchable Opacity(Custom UI for facebook login Button)
<TouchableOpacity // This is in the Main Screen
onPress={() => {
this.goFacbookLogin()
}}
</TouchableOpacity>
Here is My Function which is being called on the onPress Event.
async goFacbookLogin(){ // This is also a function on the Main Screen
await FBLogin.facebookLogin().then((data) =>{
alert("Data: " + JSON.stringify(data));
});
}
Now The FBLogin is basically a service for login as facebook. and it has a function facbookLogin()
import React, { Component } from 'react';
import { View, Button, Dimensions } from 'react-native';
import {LoginManager, LoginButton,
AccessToken,GraphRequest,GraphRequestManager } from 'react-native-fbsdk';
class FBLoginButton extends Component{// This is Implemented in another File
constructor(props)
{
super(props);
this.state =
{
//screenWidth: Dimensions.get("window").width,
//screenHeight: Dimensions.get("window").height,
UserProfileInformation:
{
UserAge: null,
Gender: null,
UserLocation: null,
ImageUrl: null,
}
}
}
async facebookLogin()
{
let _result;
try
{
LoginManager.setLoginBehavior('NATIVE_ONLY');
_result = await LoginManager.logInWithReadPermissions(['public_profile',
"email","user_birthday", "user_gender","user_hometown"]);
console.warn("Done");
}
catch (nativeError)
{
try
{
LoginManager.setLoginBehavior('WEB_ONLY');
_result = await LoginManager.logInWithReadPermissions(['public_profile',
"email", "user_birthday", "user_gender", "user_hometown"]);
}
catch (webError)
{
alert("Function Called Web Error");
}
}
if (_result.isCancelled)
{
alert('Login Cancel')
}
else
{
global.res1 = "None";
const data = await AccessToken.getCurrentAccessToken();
const infoRequest = new GraphRequest
(
'/me',
{
accessToken: data.accessToken,
parameters:
{
fields:
{
string: 'first_name, name, gender, birthday, email, hometown'
}
}
},
(error, result) => {
if (error)
{
alert("error: " + JSON.stringify(error));
}
else
{
let UserProfileInformation = this.state.UserProfileInformation;
UserProfileInformation['Gender'] = result.gender;
UserProfileInformation['UserLocation'] = result.hometown.name;
this.setState({ UserProfileInformation: UserProfileInformation}, ()
=> console.warn("DDDDD: " + this.state.UserProfileInformation));
res1 = result;
}
}
);
new GraphRequestManager().addRequest(infoRequest).start()
return(this.state.UserProfileInformation);
}
//return "Done";
}
}
export default FBLogin = new FBLoginButton();
Now the Problem Here is that when Graph Request Starts the InfoRequest it Fetches the Data successfully in the callback function but i have no idea how to return it to the main function call in which i have implemented then statement after the function call. Currently The data gets stored in a state but as state takes time to change the return statement executed early and i got null but when i press the opacity button second time, the data is correct as it's previous state was changed.
i have tried global and let variables but they got destroyed after the callback function ends.
In Short All I want is to return this result to the facebookLogin in the Main Screen.
i am New in React Native so If any one can help it would really be appreciated.
Would (really) appreciate help on this one.
I have a realm listView which navigates away to a detailed view.
The detailed view is deleting the entry from the original list view and navigate back. I have registered listeners on the realm DB change to update the content of the list view.
Here is the code I'm using, which, after delete get an exception - "Accessing object of type Contact which has been invalidated or deleted" after the navigation occurs.
Does anyone have an idea why?
Also, it seems that the change listener (updateContactsFromDB) is called twice, while deleting just one object - ideas?
10x
ContactPage.js:
export default class ContactsPage extends Component {
updateContactsFromDB(){
console.log("ContactsPage:updateContactsFromDB()");
let contacts = Realm.objects('Contact');
this.setState({
dataSource: this.state.dataSource.cloneWithRows(contacts.snapshot()),
});
}
constructor(props) {
console.log("ContactsPage:constructor()");
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => (r1 !== r2)});
let contacts = Realm.objects('Contact');
this.state = {
dataSource: ds.cloneWithRows(contacts.snapshot()),
};
this.updateContactsFromDB = this.updateContactsFromDB.bind(this);
}
componentWillMount(props){
console.log("ContactsPage:componentWillMount");
Realm.addListener('change', this.updateContactsFromDB);
}
componentWillUnmount(props){
console.log("ContactsPage:componentWillUnmount");
Realm.removeListener('change', this.updateContactsFromDB);
}
render() {
console.log("ContactsPage:render()");
return (
<ListView
dataSource={this.state.dataSource}
renderRow={(contact) => (
<TouchableOpacity onPress={ () => this.props.navigation.navigate('ContactNotesPage', { contact: contact}) }>
<Text>test Contact</Text>
</TouchableOpacity>
)
}
/>
);
}
}
ContactNotesPage.js:
export default class ContactNotesPage extends Component {
constructor(props) {
console.log("ContactNotesPage:constructor");
super(props);
}
render(){
console.log("ContactNotesPage:render()");
const { params } = this.props.navigation.state;
return (
<TouchableOpacity onPress={ () => {
console.log("ContactNotesPage:delete");
Realm.write(() => { Realm.delete(params.contact);});
this.props.navigation.navigate('ContactsPage');
}
}>
<Text>DeleteContact</Text>
</TouchableOpacity>
)
}
};
// main.js
const MainStack = StackNavigator({
ContactsPage: {
screen: ContactsPage,
},
ContactNotesPage:{
screen: ContactNotesPage,
},
});
export default MainStack;
Seems like it's a bug in realm/react-navigation. when passing a parameter which is a realm object - if you delete this object your next navigation will fail.
A bug is already open at https://github.com/realm/realm-js/issues/1031
This happens when you get an object from realm (using realm.objects('collection')) and use realm.close() function on it. Either use Object.assign method or destructuring object or array approach to mitigate this problem. Like below,
const realmObject = realm.objects('collection');
const obj = [...realmObject] or {...realmObject[0]}; // the later one when filtered is used on realmObject.
My solution was that I did not call realm.close() because I was constantly engaged with the database.
https://realm.io/docs/javascript/0.14.0/api/Realm.html#close
i used useFocusEffect from #react-navigation/native after delete item in array for update list array like this
//state to check get data ability:
const [getDataAbility, setGetDataAbility] = useState(true);
//useFocusEffect
useFocusEffect(
React.useCallback(() => {
if (getDataAbility === true) {
// handle data
}
}, [ dependencies... ]),
);
// catch error ""accessing obj of..."
try {
console.log(pickingItem.id);
} catch (error) {
setGetDataAbility(false);
console.log('err picking: ', error);
}
-> solve
This is work for me:
async function updateRealm() {
const realm = Realm.open({
schemas: [
/*myschemas*/
]
});
realm.addListener('change', () => {
/* consult function */
});
}
My code is
const main = () => {
let caption;
AsyncStorage.getItem("XXX", (err, result) => {
caption = <View>...</View>
});
render (
...
{caption}
...
);
}
But I got an error as below.
RawText "" must be wrapped in an explicit <Text> component.
I'm going to assume that, based on your pseudo-code, you understand how to get data from AsyncStorage, that it's not a good idea to be using AsyncStorage inside your render function, and that you don't actually mean ajax but rather local storage.
But the error is showing up because you need to make sure you wrap text inside a <Text> element. If you look at this paragraph it says:
In React Native, we are more strict about it: you must wrap all the text nodes inside of a <Text> component; you cannot have a text node directly under a <View>.
EDIT:
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
data: '',
};
}
componentDidMount() {
AsyncStorage.getItem('XXX', (err, result) => {
// #TODO: You should handle errors too
this.setState({
data: result.text,
});
});
}
render() {
// Returning null will not render anything
// Once the results come in, it will update automatically
if (!this.state.data) return null;
// Raw text must be wrapped in Text
return (
<Text>{this.state.data}</Text>
);
}
}
You can try to stock data in a state and display your component whit a function:
AsyncStorage.getItem("XXX", (err, result) => {
this.setState({result:result})
});
function element(){
if([check if your state is not null])
return(
<View>... {this.state.result} ...</View>
)
}
render (
...
{this.element()}
...
);