React-native saving image as state not working - javascript

First of all, I am using Image Picker to get the image and RNFetchBlob to sending in the API.
My problem is that I have some text inputs and in the end pushing a button, I would like to send this inputs with the image at the same time. For that I trying to save the image uri as state to send it after. But appears _this2.setState is not a function. For the moment I don't know how to solve it.
And this is my code simplified.
class Create extends Component{
constructor(props){
super(props);
this.state = {
foto: '',
}
}
openImagePicker(){
const options = {
title: 'Select Avatar',
storageOptions: {
skipBackup: true,
path: 'images'
}
}
ImagePicker.showImagePicker(options, (imagen) =>{
if (imagen.didCancel) {
console.log('User cancelled image picker');
}
else if (imagen.error) {
console.log('ImagePicker Error: ', imagen.error);
}
else if (imagen.customButton) {
console.log('User tapped custom button: ', imagen.customButton);
}
else {
let source = { uri: imagen.uri };
console.log(source.uri);
this.setState({
foto: source.uri
})
}
render(){
return(
<Container>
<Header><Title>Eat</Title></Header>
<Content>
<Text>Título</Text>
<View>
<TextInput
onChangeText={(text) => this.titulo = text}
/>
</View>
<Text>Personas</Text>
<View style={styles.container}>
<TextInput
type="number"
onChangeText={(text) => this.personas = text}
/>
</View>
<Text>Foto</Text>
<View>
<TouchableOpacity activeOpacity={.8} onPress={this.openImagePicker}>
<View>
<Text>Cambiar</Text>
</View>
</TouchableOpacity>
</View>
<View>
<ScrollView >
<TouchableHighlight onPress={(this._create.bind(this))}>
<Text>Crear Receta</Text>
</TouchableHighlight>
</ScrollView>
</View>
</Content>
</Container>
)
}
_create () {
RNFetchBlob.fetch('POST', 'XXXXXXXXXXXX', {
'Content-Type' : 'multipart/form-data',
},
[
// element with property `filename` will be transformed into `file` in form data
{ name : 'file', filename : 'avatar-foo.png', type:'image/foo', data: RNFetchBlob.wrap(source.uri)},
{ name : 'info', data : JSON.stringify({
"titulo": this.titulo,
"personas": this.personas,
})}
]).then((resp) => {
console.log("ok");
}).catch((err) => {
console.log(err);
})
}
})
}
}

You can either bind your method openImagePicker or call it like this:
onPress={() => this.openImagePicker()}
So that you can access your class context.

Related

How to successfully pass params between screens?

I have been reading a lot of answers on SO as well as GitHub issues trying to implement a solution, but have been unable to come up with a solution for this situation.
Here is a representation of my code:
class MeetingsScreen extends React.Component {
constructor(props) {
super(props)
this.state = {
upcomingMeeting: [],
refreshing: false
};
}
async handlePress() {
const user = await AsyncStorage.getItem('User');
this.props.navigation.navigate('WriteSummary', { id: user.id, type: 'submit' });
}
printUpcomingMeetings = () => {
return (<View style={styles.meeting}>
<Button
containerStyle={styles.meetingButton}
style={styles.meetingButtonText}
onPress={() => this.handlePress(user.id)}
Press me to write summary!
</Button>
</View>);
}
}
render () {
return (<View style={{flex:1}} key={this.state.refreshing}>
{ this.printUpcomingMeetings() }
</View>);
}
}
class WriteSummaryScreen extends React.Component {
constructor(props) {
super(props)
this.state = {
storageId: '',
normalId: -1,
type: '',
curSummary: ''
}
}
componentDidMount = () => {
const { params } = this.props.navigation.state;
const { id, type } = params ? params : null;
const storageId = 'summary_' + id;
this.setState({storageId:storageId});
this.setState({normalId:id});
this.setState({type:type});
AsyncStorage.getItem(storageId).then((value) => this.setSkipValue(value));
}
async setSkipValue (value) {
if (value !== null) {
this.setState({ 'curSummary': value });
} else {
this.setState({ 'curSummary': '' });
}
}
async saveSummary (text) {
this.setState({'curSummary': text});
await AsyncStorage.setItem(this.state.storageId, text);
}
async handleSubmit() {
const user = await AsyncStorage.getItem('User');
if (this.state.type === 'submit') {
// post insert
const postres = fetch (url + '/create-summary', {
method: 'POST',
body: JSON.stringify({
AppointmentId: this.state.normalId,
SummaryText: this.state.curSummary,
UserId: user.Id
}),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
})
.catch((error) => {
console.error(error);
});
} else {
// post update
const postres = fetch (url + '/update-summary', {
method: 'POST',
body: JSON.stringify({
AppointmentId: this.state.normalId,
SummaryText: this.state.curSummary,
UserId: user.Id
}),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
})
.catch((error) => {
console.error(error);
});
}
this.props.navigation.navigate('Meetings');
}
render () {
return <View style={{flex:1}}>
<View>
<View style={{height:25, backgroundColor: colors.vikingBlue}}></View>
<View style={{height:30, backgroundColor: colors.white}}></View>
<View style={{flexDirection:'row', backgroundColor: colors.white, alignItems:'center'}}>
<View style={{width:5}}></View>
<TouchableOpacity onPress={() => this.props.navigation.goBack()} activeOpacity={0.5}>
<Image style={{width:30, height:30}} source={require('./assets/icons8-back-50.png')} />
</TouchableOpacity>
<View style={{width:10}}></View>
<View style={{width:mainTitleWidth,textAlign:'center',alignItems:'center'}}>
<Text style={{fontSize:22}}>Settings</Text>
</View>
<TouchableOpacity onPress={() => this.props.navigation.navigate('HelpModal')} activeOpacity={0.5}>
<Image style={{width:30, height:30}} source={require('./assets/help.png')} />
</TouchableOpacity>
</View>
<View style={{height:30, backgroundColor: colors.white}}></View>
</View>
<TextInput
multiline
numberOfLines={6}
style={styles.summaryInput}
onChangeText={text => saveSummary(text)}
value={this.state.curSummary} />
<Button
containerStyle={styles.summaryButton}
style={styles.summaryButtonText}
onPress={this.handleSubmit()}>
Submit
</Button>
</View>
}
}
function HomeStack() {
return (
<Tab.Navigator
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Meetings" component={MeetingsScreen} />
<Tab.Screen name="Topics" component={TopicsScreen} />
</Tab.Navigator>
);
}
export default class AppContainer extends React.Component {
// Main rendering function. Always begins on the SplashScreen, which checks user login status and directs to Meetings. I left it out and the other Tab navigator screens for less clutter.
render() {
return (
<NavigationContainer>
<Stack.Navigator headerMode='none' initialRouteName='Splash'>
<Stack.Screen name='Splash' component={SplashScreen} />
<Stack.Screen name='Main' component={HomeStack} />
<Stack.Screen name='WriteSummary' component={WriteSummaryScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
};
I get the error TypeError: undefined is not an object (evaluating '_this7.props.navigation.state.params)' after pressing the button on MeetingsScreen and navigating to WriteSummaryScreen.
What confuses me is the screen is navigated to, so the data should have been passed. What am I missing?
You can access parametes like below if its class based component:
class WriteSummaryScreen extends React.Component {
const {id, type} = this.props.route.params;
//........
for the functional component:
const WriteSummaryScreen =({navigation, route})=>{
const {id, type} = route.params;
//..........
}
You can access the params that you have passed from a screen using the getParam function.
so you can use this code:
const id = this.props.navigation.getParam("id");
const type = this.props.navigation.getParam("type");
What ended up working for me was the following:
const id = this.props.route.params.id;
const type = this.props.route.params.type;

How to Show Activity Indicator Until data if Fetched from Firebase Using Redux

I am Using FireBase as a Database for fetching data in a react-native app using Redux. I want to Show an Activity Indicator until the data is been fetched.
Here is my code Redux :
export function getHome() {
const request = axios({
method: "GET",
url: `${FIREBASEURL}/home.json`
})
.then(response => {
const articles = [];
for (let key in response.data) {
articles.push({
...response.data[key],
id: key
});
}
return articles;
})
.catch(e => {
return false;
});
return {
type: GET_HOME,
payload: request
};
}
Here is my React Native code where data will be shown:
import React, { Component } from "react";
import {
StyleSheet,
View,
Text,
ScrollView,
ActivityIndicator,
TouchableWithoutFeedback,
Image
} from "react-native";
import { connect } from "react-redux";
import { getHome } from "../store/actions/home_actions";
import DemoScreen from "./rn-sound/demo";
class HomeScreen extends Component {
componentDidMount() {
this.props.dispatch(getHome());
}
renderArticle = imgs =>
imgs.articles
? imgs.articles.map((item, i) => (
<TouchableWithoutFeedback
onPress={() => this.props.navigation.navigate(`${item.navigate}`)}
key={i}
>
<View>
<View>
<Image
style={{
height: 220,
width: "100%",
justifyContent: "space-around"
}}
source={{ uri: `${item.image}` }}
resizeMode="cover"
/>
</View>
<View>
<Text >{item.name}</Text>
</View>
<View>
<Text }>{item.tagline}</Text>
</View>
</View>
</TouchableWithoutFeedback>
))
: null;
render() {
return (
<ScrollView}>
{this.renderArticle(this.props.Home)}
</ScrollView>
);
}
}
how to show Activity Indiactor Untill my data from firebase is been Fetched
You can use loading variable in state. You have set false it before fetch command after that set to true. You can see below sample.
constructor(props) {
super(props);
this.state = {
loading: false
};
}
componentDidMount = () => {
this.setState({
loading: true
})
this.props.dispatch(getHome()).then(response=>{
this.setState({
loading: false
})
})
}
render() {
return (
<ScrollView}>
{this.state.loading == false ? (
<View>
{this.renderArticle(this.props.Home)}
</View>
) : (
<ActivityIndicator size="large" />
)}
</ScrollView>
);
}

How do I let a screen know other screen's state

Now I am trying to let a screen containing some lists know other screen's state.
The main screen contains some Form Components which user can input text to.
Other screens, Form Components contain some TextInput forms.
If a user inputs some texts into some form and then if this user puts a back button or a save-button, I want to change the main screen's state of these form components to be like "Editing", "Not Edit" or "Complete".
How to change the state of each form component's state on the Main screen?
This picture is the main screen. Please focus on Red characters. There will be changed if a user will input some text into a form on other screens.
This is a input-screen
handleOnpress() {
const db = firebase.firestore();
const { currentUser } = firebase.auth();
db.collection(`users/${currentUser.uid}/form1`).add({
form1 : [
{ fullname: this.state.fullname },
{ middleName: this.state.middleName },
{ reason: this.state.reason },
{ birthPlaceCity: this.state.birthPlaceCity },
{ birthPlaceCountry: this.state.birthPlaceCountry },
{ Citizinchip: this.state.Citizinchip },
{ aboutMaridge: this.state.aboutMaridge },
{ fromTermOfMaridge: this.state.fromTermOfMaridge },
{ ToTermOfMaridge: this.state.ToTermOfMaridge },
{ nameOfSpouse: this.state.nameOfSpouse },
{ birthdateOfSpouse: this.state.birthdateOfSpouse },
{ fromTermOfExMaridge: this.state.fromTermOfExMaridge },
{ ToTermOfExMaridge: this.state.ToTermOfExMaridge },
{ nameOfExSpouse: this.state.nameOfExSpouse },
{ birthdateOfExSpouse: this.state.birthdateOfExSpouse }]
})
.then(() => {
this.props.navigation.goBack();
})
.catch((error) => {
console.log(error);
});
}
render() {
return (
<ScrollView style={styles.container}>
<InfoHeader navigation={this.props.navigation}>申請者情報1
</InfoHeader>
<Notes />
<QuestionTextSet onChangeText={(text) => { this.setState({
fullname: text }); }} placeholder={'例:留学太郎'}>姓名(漢字表記)
</QuestionTextSet>
<QuestionTextSet onChangeText={(text) => { this.setState({
middleName: text }); }}>本名以外に旧姓・通称名(通名)・別名など他の名前が
あればローマ字で記入</QuestionTextSet>
<QuestionTextSet onChangeText={(text) => { this.setState({
reason: text }); }} placeholder={'例:結婚・離婚/ご両親の離婚のためな
ど'}>別名がある方はその理由</QuestionTextSet>
<SubmitButton style={styles.saveButton} onPress=
{this.handleOnpress.bind(this)}>保存</SubmitButton>
<Copyrights />
</ScrollView>
);
This is the main screen.
class WHApply extends React.Component {
render() {
return (
<ScrollView style={styles.container}>
<WHApplyBar navigation={this.props.navigation} />
<WHApplyIndexBar />
<HWApplyMailBar />
<HWApplyList navigation={this.props.navigation} />
<Agreement />
<SubmitButton>同意して送信</SubmitButton>
<Logout />
<Copyrights />
</ScrollView>
);
}
}
And this is a code of HWApplyList.
This component is import to the main screen.
class HWApplyList extends React.Component {
state = {
fontLoaded: false,
}
async componentWillMount() {
await Font.loadAsync({
FontAwesome: fontAwesome,
});
this.setState({ fontLoaded: true });
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity onPress={() => {
this.props.navigation.navigate('PersonalInfo1'); }} >
<View style={styles.listBox}>
<Text style={styles.listBoxText}>
申請者情報1
</Text>
<View style={styles.inputBotton}>
<Text style={styles.inputBottonText}>未入力</Text>
</View>
<View style={{ flex: 1, justifyContent: 'center' }}>
{
this.state.fontLoaded ? (
<View style={styles.navigationButton}>
<Text style={styles.navigationButtonIcon}>{'\uf0da'}
</Text>
</View>
) : null
}
</View>
</View>
</TouchableOpacity>
In addition to the previous answer, you will also need to pass a handler function to the screen child. This handle function will allow your child screen to modify the parent's state.
Something like
onChange(value) {
this.setState({value});
}
render() {
return (
<Screen1 value={this.state.value} onChange={this.onChange}/>
<Screen2 value={this.state.value}/>
)
}
When you start to make many components that uses each other's state, Using a tool like "redux" makes it much easier.
Instead of putting your data ( that's you want it to be shared with the other components ) in a particular component state. you can create a common state for the whole app and you can access this state from any component.
here's a good article:
Understanding Redux: The World’s Easiest Guide to Beginning Redux

Uncaught TypeError: Cannot read property _currentElement of null

I have an input that i'm trying to format/mask as a currency. I'm using a package to accomplish this (CurrencyInput). I keep getting this error on my component Uncaught TypeError: Cannot read property _currentElement of null and I don't understand why. If I use React's TextInput it works correctly. I've tried 10 different packages from NPM and I keep getting the same error. How can I find out what's causing this?
I've checked responses of similar issues posted.... The answers I've seen don't seem apply to my situation. My components are loaded before mounting, no undefined props (from what I can see) and I'm on a stable version of React.
class Profile extends React.PureComponent {
static propTypes = {
navigation: PropTypes.object,
handleLogout: PropTypes.func,
handleaciNav: PropTypes.func,
user: PropTypes.object
};
static navigationOptions = {
headerVisible: true,
title: 'Profile'
};
constructor(props) {
super(props);
this.onCreditCardChange = this
.onCreditCardChange
.bind(this);
this.onCreditCardFocus = this
.onCreditCardFocus
.bind(this);
this.state = {
data: [],
loading: true
};
}
getInitialState(){
return ({amount: "0.00"});
}
handleChange(event, maskedvalue, floatvalue){
this.setState({amount: maskedvalue});
}
componentDidMount() {
fetch("www.some.com", {method: 'get'})
.then(response => response.json())
.then(data => {
this.setState({data: data});
})
.catch(function (err) {
//
})
}
callBoth = () => {
console.log(this.state.data)
}
render() {
if (this.state.data.length === 0) {
return null
}
return (
<ContentWrapper>
<View style={styles.container}>
<View style={styles.header}>
{jsonData}
<TouchableHighlight
underlayColor='transparent'
style={styles.btn}
onPress={this.goToAcibtnTwo}>
<Image source={images.prredbtn}></Image>
</TouchableHighlight>
<Text style={styles.textthingsmall}>{'Must be Paid Immediately'}</Text>
{jsonData2}
<TouchableHighlight
underlayColor='transparent'
style={styles.btn}
onPress={this.goToAcibtnTwo}>
<Image source={images.prgreenbtn}></Image>
</TouchableHighlight>
<Text style={styles.textthingsmall}>{'May include next payment or other fees'}</Text>
{jsonData3}
<TouchableHighlight
underlayColor='transparent'
style={styles.btn}
onPress={this.goToAcibtnTwo}>
<Image source={images.prgreenbtn}></Image>
</TouchableHighlight>
{jsonData4}
<View>
<CurrencyInput value={this.state.amount} onChangeEvent={this.handleChange}/>
</View>
</View>
<View>
<Image style={styles.btmicons} source={images.optionsroundbtn}/>
</View>
</View>
</ContentWrapper>
);
}
}
export default Profile;

how to render objects in react native?

I am working on a simple to do application linked to firebase using react native. i have one class with a few methods in it,as far as I can tell by searching online about this problem, it seems to be something related to an output in the render function. but i checked my methods and I am unable to pin down the exact problem.
and this is my class:
class ToDo_React extends Component {
constructor(props) {
super(props);
var myFirebaseRef = new Firebase(' ');
this.itemsRef = myFirebaseRef.child('items');
this.state = {
newTodo: '',
todoSource: new ListView.DataSource({rowHasChanged: (row1, row2) => row1 !== row2})
};
this.items = [];
}
componentDidMount() {
// When a todo is added
this.itemsRef.on('child_added', (dataSnapshot) => {
this.items.push({id: dataSnapshot.key(), text: dataSnapshot.val()});
this.setState({
todoSource: this.state.todoSource.cloneWithRows(this.items)
});
});
this.itemsRef.on('child_removed', (dataSnapshot) => {
this.items = this.items.filter((x) => x.id !== dataSnapshot.key());
this.setState({
todoSource: this.state.todoSource.cloneWithRows(this.items)
});
});
}
addTodo() {
if (this.state.newTodo !== '') {
this.itemsRef.push({
todo: this.state.newTodo
});
this.setState({
newTodo : ''
});
}
}
removeTodo(rowData) {
this.itemsRef.child(rowData.id).remove();
}
render() { return (
<View style={styles.appContainer}>
<View style={styles.titleView}>
<Text style={styles.titleText}>
My Todos
</Text>
</View>
<View style={styles.inputcontainer}>
<TextInput style={styles.input} onChangeText={(text) => this.setState({newTodo: text})} value={this.state.newTodo}/>
<TouchableHighlight
style={styles.button}
onPress={() => this.addTodo()}
underlayColor='#dddddd'>
<Text style={styles.btnText}>Add!</Text>
</TouchableHighlight>
</View>
<ListView
dataSource={this.state.todoSource}
renderRow={this.renderRow.bind(this)} />
</View>
);
}
renderRow(rowData) {
return (
<TouchableHighlight
underlayColor='#dddddd'
onPress={() => this.removeTodo(rowData)}>
<View>
<View style={styles.row}>
<Text style={styles.todoText}>{rowData.text}</Text>
</View>
<View style={styles.separator} />
</View>
</TouchableHighlight>
);
}
}
The issue is with your renderRow method where you render:
<Text style={styles.todoText}>{rowData.text}</Text>
Here you are passing an object as a children of Text element instead of a string. That is because you're setting an object under the text key for your data store here:
this.items.push({id: dataSnapshot.key(), text: dataSnapshot.val()});
Note that val() here is a reference to an object you've pushed to your firebase instance here (see firebase docs):
this.itemsRef.push({
todo: this.state.newTodo
});
So what you perhaps want to do here is to just push this.state.newTodo instead of an object.

Categories