React Native - Defined State not able to be use - javascript

This Screen was navigate from other Screen. When this screen is open, it will go to load some data from Webhost database and fill them into the View Controls. There are few variable is defined in the Constructor(State), but when the program trying to call the ShowAll() function (click the Show Button) to display an Alert Message with the content of a defined variable in state, the program display this error.
Error: undefined is not an object (evaluating 'this.state.RecipeName')
export default class SecondActivity extends Component
{
static navigationOptions = { title: 'View Details',};
constructor(props) {
super(props)
this.state={
RecipeID : '',
RecipeName : '',
RecipeType : '',
RecipeIngredient : '',
RecipeStep : ''
}
}
componentDidMount(){
fetch('https://unsurfaced-cross.000webhostapp.com/getRecipeDetailRN.php', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
// Getting the id.
RecipeID: this.props.navigation.state.params.ListViewClickItemHolder
})
}).then((response) => response.json())
.then((responseJson) => {
this.setState({
RecipeID : responseJson[0].RecipeID,
RecipeName : responseJson[0].RecipeName,
RecipeType : responseJson[0].RecipeType,
RecipeIngredient : responseJson[0].RecipeIngredient,
RecipeStep : responseJson[0].RecipeStep
})
}).catch((error) => {
console.error(error);
});
}
ShowAll(){
Alert.alert(
'Alert Title',
this.state.RecipeName, // **ERROR COME FROM THIS LINE**
[
{text: 'Ask me later', onPress: () => console.log('Ask me later pressed')},
{text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel'},
{text: 'OK', onPress: () => console.log('OK Pressed')},
],
{ cancelable: false }
)
}
render()
{
return(
<View style = { styles.MainContainer }>
<View style={{flex:1, flexDirection: 'column'}} >
<Text style={styles.textViewContainer} > {'Recipe ID = ' + this.state.RecipeID} </Text>
<Text style={styles.textViewContainer} > {'Recipe Name = '} </Text>
<TextInput
style={styles.textInput}
onChangeText={(text) => this.setState({RecipeName: text})}
value={this.state.RecipeName}
/>
<Text style={styles.textViewContainer} > {'Recipe Type = '} </Text>
<Picker
style={{width: 200}}
selectedValue={this.state.RecipeType}
onValueChange={
(itemValue, itemIndex) => {
this.setState({RecipeType: itemValue, isLoading:true})
this.componentDidMount()
this.forceUpdate()
}
}>
<Picker.Item label="Vegetarian" value="Vegetarian" />
<Picker.Item label="Fast Food" value="Fast Food" />
<Picker.Item label="Healthy" value="Healthy" />
<Picker.Item label="No-Cook" value="No-Cook" />
<Picker.Item label="Make Ahead" value="Make Ahead" />
</Picker>
<Text style={styles.textViewContainer} > {'Ingredient = '} </Text>
<TextInput
style={styles.textInput}
onChangeText={(text) => this.setState({RecipeIngredient: text})}
value={this.state.RecipeIngredient}
/>
<Text style={styles.textViewContainer} > {'Step = '} </Text>
<TextInput
style={styles.textInput}
onChangeText={(text) => this.setState({RecipeStep: text})}
value={this.state.RecipeStep}
/>
<Button
title="Show"
onPress={this.ShowAll}
color="#841584"
/>
</View>
</View>
);
}
}
The RecipeName Variable was already defined in Consturctor(State), it also able to be used/called by the Views too. But when come to the ShowAll() function, it become Undefined.
Why/How is would become undefined when the Code is already there?

Binding in Render
onChange={this.handleChange.bind(this)}
If you use an ES6 class and you want to call your function in render, React no longer autobinds. One way to resolve this is to call bind in render. But this approach actually not good. Because the function is reallocated on every render. So, there are several ways to do it.
Use Arrow Function in Render
This approach is similar to binding in render approach. You can avoid changing the this context by using an arrow function in render:
onChange={e => this.handleChange(e)}
This is another approach to do the same thing but the performance still same with binding in render. Only difference is you are not using 'this'.
Bind in Constructor
One way to avoid binding in render is to bind in the constructor.
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
This is the approach currently recommended in the React docs for “better performance in your application”. You just bind once and use every time you press the button inside your render.

Related

when i call a function in onPress method it's show error " undefined is not a function" in react native

I can't be able to call a function inside component "onPress" event in a same class, it's showing me an error like " undefined is not a function" how to solve this
here is my code overview
class UserList extends Component {
constructor(props) {
super(props);
this.state = {
user_data:[],
};
}
componentDidMount() {
}
logOut=(ext_id)=>
{
console.log(ext_id);
}
UserItem (props,{MainScreen}){
const navigation = useNavigation();
const [name, setName] = useState();
const [image_name, setImg] = useState();
fetch('https://exmaple.com/info.php', {
method: 'POST',
mode: 'no-cors',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
'user_id': props.one,
'key':props.three,
'api_key':`XXXXXXXX`
})
}).then((response) => response.json())
.then((json) => {
console.log(json);
setName(json.business_name);
setImg(json.logo);
}).catch((err) => { console.log(err); });
return(
<TouchableRipple
onPress={() => {navigation.navigate('MainScreen',{one:props.one,two:props.two,three:props.three})}}
rippleColor="rgba(0, 0, 0, .1)"
centered={true}
borderless={true}
style={styles.ripple}
>
<Card style={{marginHorizontal:20, marginVertical:10, padding:8, flexDirection:'row'}}>
<View style={{flexDirection:'row'}}>
<Image
style={{height:80,width:80,borderRadius:8}}
source={{
uri:`https://tridevmart.com/web_app/shop_logo/${image_name}`
}} />
<View style={{flex:1}}>
<Text numberOfLines = {1} style={{fontSize:24,margin:10}}>{name}</Text>
<View style={{flexDirection:'row',justifyContent:'space-between'}}>
<Text numberOfLines={5} style={{fontSize:16,marginHorizontal:10,flex:1,color:`#ccc`}}>Income : 2654</Text>
<Button icon="logout" mode="contained" mode="text" onPress={this.logOut(props.ext)}>Logout</Button>
</View>
</View>
</View>
</Card>
</TouchableRipple>
);
}
list = () => {
return this.state.user_data.map((element) => {
return (
<this.UserItem key={element.ext_id} ext={element.ext_id} one={element.one} two={element.two} three={element.three}/>
);
});
};
render(){
return(
<SafeAreaView style={{ flex: 1 }}>
<View style={styles.mainBox}>
{this.list()}
<Button mode="contained" style={styles.inputButton} onPress={() => this.props.navigation.navigate('LoginScreen')}>+ Add More Account</Button>
</View>
</SafeAreaView>
);
}
}
In here let's see "UserItem" component. and inside this see "Logout" Button. in onPress event of logout button show me the error of "undefined is not a function"
Please try to solve my error
I think the error is related to "this" relationship with arrow functions inside a class component in React.
Try replacing the arrow function to a normal function
logOut(){console.log("clicked")}
You're supposed to pass a function to the event handlers in react, not call the function.
Instead of
onPress={this.logOut(props.ext)
you should be doing
onPress={() => this.logOut(props.ext)
If you don't have anything to pass into the function you can simply do onPress={this.logOut}. Keep in mind that the event object will be passed to this.logOut as the first argument.
Edit:
Move the UserItem outside the other component:
function UserItem (props,{MainScreen}){
...
return(
...
<Button icon="logout" mode="contained" mode="text" onPress={() => props.logOut(props.ext)}>Logout</Button>
</View>
...
);
}
class UserList extends Component {
...
list = () => {
return this.state.user_data.map((element) => {
return (
<UserItem key={element.ext_id} ext={element.ext_id} one={element.one} two={element.two} three={element.three} logOut={this.logOut}/>
);
});
};
...
}

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

Get the value from TextIput

I want to be able to retrieve the value of the text after the user presses enter/return.
I tried the following ...
class HomePage extends Component {
constructor(props) {
super(props);
this.state = {
text:''
}
}
callTextSubmit = (val) => {
console.log('callTextSubmit ', val);
};
render() {
return (
<View>
<TextInput
style={{height: 40, borderColor: 'gray', borderWidth: 1}}
onChangeText={(text) => this.setState({ text })}
onSubmitEditing={(text) => this.callTextSubmit(text)}
value={this.state.text}
/>
</View>
)
}
}
This returns the following... from the console log.
Proxy {dispatchConfig: {…}, _targetInst: FiberNode, isDefaultPrevented: ƒ, isPropagationStopped: ƒ, _dispatchListeners: ƒ, …}
>[[Handler]]:Object
>[[Target]]:Class
>[[IsRevoked]]:false
I want to access the value from the text input, is there a workaround to this? Thank you
The argument for the onSubmitEditing handler is the event, not the current input.
Since you set the state variable text each time the TextInput is changed, you can just use the text variable in your state instead.
callTextSubmit = () => {
console.log('callTextSubmit ', this.state.text);
};
Since you are using this line to add the TextInput's value to state
onChangeText={(text) => this.setState({ text })}
The reasonable way would be to get the value from state on your onPress function on a Button (TouchableOpacity/TouchableHighlight) added to your View like this:
<TouchableOpacity onPress={()=>console.log(this.state.text)}>
<Text>
Enter
</Text>
</TouchableOpacity>
Here is code for your Component
class HomePage extends Component {
constructor(props) {
super(props);
this.state = {
text:''
}
}
callTextSubmit = () => {
console.log(console.log(this.state.text));
};
render() {
return (
<View>
<TextInput
style={{height: 40, borderColor: 'gray', borderWidth: 1}}
onChangeText={(text) => this.setState({ text })}
value={this.state.text}
/>
<TouchableOpacity onPress={()=>console.log(this.state.text)}>
<Text>
Enter
</Text>
</TouchableOpacity>
</View>
)
}

React-native saving image as state not working

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.

React (native) navigation toggle search bar

Have been struggling a couple of days now trying to figure out how to toggle a search bar in the react navigation.
My approach has been to
static navigationOptions = ({navigation}) => {
return {
title: 'Header Title',
headerLeft: (
{navigation.params.state.search ? <searchfield query={text => navigation.setParams(text)} > : <settings>}
),
headerRight: (
<TouchableOpacity style={{ marginHorizontal: 10 }}>
<Icon name="search" size={28} color="#5751D9" />
</TouchableOpacity>
)
}}
I then wanted to add some logic to the headerLeft so it either returns the cog icon button component or an TextInput component (plan to pass the text to setParams and use it as a filter in the list component below the header) but I can't seem to figure out how to pass down a state or state handler as props when I'm not navigating to it.. It's the initial screen.
Hook a function to your setParams inside componentDidMount which will
be called on searchedText change, use this function to setState.
componentDidMount() {
this.props.navigation.setParams({onSearchText: (searchedText) => this.onSearchText(searchedText)});
}
onSearchText(searchedText) {
//update your list using this searchedText
this.setState({searchedText})
}
Now call the function onSearchText() when searchedText changes,
static navigationOptions = ({navigation}) => {
return {
title: 'Header Title',
headerLeft: (
{navigation.params.state.search ? <searchfield query={text => onSearchText(text)} > : <settings>}
),
headerRight: (
<TouchableOpacity style={{ marginHorizontal: 10 }}>
<Icon name="search" size={28} color="#5751D9" />
</TouchableOpacity>
)
}}
Hope it will help you ...

Categories