this.setState not changing state when using onChangeText React Native - javascript

I've tried a few methods to get setState() to update the value of state. Currently the text in the <TextInput> changes, but the value in this.state doesn't change.
I have the console.log in the right place, I've tried writing external functions, I've messed around with the variable's names but nothing seems to work.
import * as React from 'react';
import { View, Text, TextInput, TouchableHighlight, Dimensions, StyleSheet } from "react-native";
import PropTypes from "prop-types";
class EditNote extends React.Component{
constructor(props){
super(props)
this.state = {
title: '',
text: '',
id: ''
}
}
// TODO: Change textboxes to match the props from the NoteList
static getDerivedStateFromProps(props){
return(
{...props.route.params}
)
}
render(){
return(
<View style={s.container}>
<View style={s.titleContainer}>
<Text style={s.titleText}>Edit Note</Text>
<View style={{flex: 1}}/>
</View>
<View style={s.inputContainer}>
<TextInput
style={{...s.input, ...s.titleInput}}
autoCapitalize='words'
keyboardAppearance='dark'
placeholderTextColor='#DDD'
onChangeText={(title) => { this.setState({title: title}, () => console.log(this.state)) }}
defaultValue={this.state.title}
/>
<TextInput
style={{...s.input, ...s.textInput}}
autoCapitalize='sentences'
keyboardAppearance='dark'
placeholderTextColor='#DDD'
multiline
onChangeText={(text) => { this.setState({text: text}, () => console.log(this.state)) }}
defaultValue={this.state.text}
/>
</View>
<View style={s.buttonContainer}>
<TouchableHighlight
style={s.backButton}
onPress={() => this.props.nav.navigate('NoteListView')}
underlayColor='#300030'
>
<Text style={s.buttonText}>Cancel</Text>
</TouchableHighlight>
<TouchableHighlight
style={s.addButton}
onPress={() => {
console.log(this.state.note)
this.props.nav.navigate('NoteListView', {note: this.state, mode: 'edit'})
}}
underlayColor='#300030'
>
<Text style={s.buttonText}>Edit</Text>
</TouchableHighlight>
</View>
</View>
)
}
}
export default EditNote

I just realized that this is a problem with two parts.
The first problem is that props.route.params is unaffected by subsequent render() calls. This means that even if you re-render the component, the same initial properties are used.
The second is getDerivedStateFromProps(). Every time the render function is called it calls getDerivedStateFromProps() right before it which sets the state to the initial route parameters.
This problem can be fixed by:
Clearing the initial route parameters in the render function after their initial use. Something a little like this at the beginning of the render()function will work. this.props.route.params = undefined
Using an if statement and a variable in state to regulate when the props should update the state.
Refactor the code to make use of the props
Option 3 is how things should be correctly done but the best solution depends on how your code works.

Related

React native, "this" keyword is undefied

so I wanted to use this.props.navigation.navigate() and I got an error message saying undefined.
after reading here in stockOverflow I saw that I needed declare a constructor like this
constructor(props) {
super(props);
}
however this keeps giving me an error saying ";" is was expected regardless of what I do, here is a simplified version of my code
const activityStyles = ActivitiesStyles.createStyles()
export default (props) => {
const {item: event, sensorID, homeInfo} = props
return (
<View style={activityStyles.linkContent} underlayColor={Colors.navigationBkgdActive}>
<View style={{flex: 0.60, flexDirection: 'row'}}>
<TouchableHighlight onPress={(event)=>{this.props.navigation.navigate("WalkThru")}}>
<SensorIcon style={iconStyle} size={Typography.bodyLineHeight} type={event.type} />
</TouchableHighlight>
<TextInput
placeholder={event.type}
autoCapitalize={true}
style={activityStyles.text}>
</TextInput>
</View>
</View>
)
}
As Emile Bergeron suggested, you should go with props since this keyword is available only for Class-Based components.
A class based component would be as follows.
export default class componentName extends Component {
constructor(props) {
super(props)
this.state = {
}
}
render() {
return (
<TouchableHighlight
<View style={activityStyles.linkContent} underlayColor={Colors.navigationBkgdActive}>
<TouchableHighlight onPress={(event)=>{this.props.navigation.navigate("WalkThru")}}>
<SensorIcon style={iconStyle} size={Typography.bodyLineHeight} type={event.type} />
</TouchableHighlight>
<TextInput
placeholder={event.type}
</TextInput>
</View>
</TouchableHighlight>
)
}
}
Furthermore, I can propose you look into differences between Stateless Functional Components and Class Components in React / React-Native.

this.setState is not a function when used at onPress

I'm fairly new to React Native and was developing an app for myself when the undefined error fired.
Like most of us, I googled away seeing what the problem might be and it seemed to be a common problem, but no fixes worked. I tried arrow functions and .bind but nothing seemed to have worked.
import React, {Fragment} from 'react';
import {
SafeAreaView,
StyleSheet,
ScrollView,
View,
Text,
StatusBar,
TouchableWithoutFeedback,
Button,
} from 'react-native';
//I'll use the other things I've imported later :-)
const App = () => {
state = {text: 'Hello World'};
return (
<View>
<Text style={styles.titleStyle}>Title Text</Text>
<TouchableWithoutFeedback
onPress={() => (this.setState({
text: 'Goodbye World',
}))}>
<View style={styles.physView}>
<Text style={styles.physTitle}>More Text</Text>
<Text style={styles.physText}>{this.state.text}</Text>
</View>
</TouchableWithoutFeedback>
</View>
);
};
The goal is simply to have the text change to Goodbye World on press but the setState is not a function error fires instead. Eventually, I'd like to have Hello World and Goodbye World switch back and fourth on click but I'm not quite past this error yet.
Thanks in advance and yes, it is dummy text.
You use functional component with state. It doesn't work like that. Functional components can't have setState method.
There are two options:
// Use class syntax
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
text: 'Hello world!',
}
}
onPress = () => {
this.setState({text: 'Bye-bye'});
}
render() {
return (
<View>
<Text style={styles.titleStyle}>Title Text</Text>
<TouchableWithoutFeedback
onPress={this.onPress}>
<View style={styles.physView}>
<Text style={styles.physTitle}>More Text</Text>
<Text style={styles.physText}>{this.state.text}</Text>
</View>
</TouchableWithoutFeedback>
</View>
);
}
};
// Use hooks
import {useState} from 'react'
const App = () => {
const [text, setText] = useState('Hello World');
return (
<View>
<Text style={styles.titleStyle}>Title Text</Text>
<TouchableWithoutFeedback
onPress={() => setText('Goodbye World');}>
<View style={styles.physView}>
<Text style={styles.physTitle}>More Text</Text>
<Text style={styles.physText}>{this.state.text}</Text>
</View>
</TouchableWithoutFeedback>
</View>
);
};
Please try this code:
<TouchableWithoutFeedback onPress={ () => this.setState({ text: 'Goodbye World' }) }>
The problem is that you just call this.setState(...) immediately when the component gets rendered for the first time.
<TouchableWithoutFeedback
onPress={this.setState({
text: 'Goodbye World',
})}>
Notice how this.setState() is executed immediately, but what you want to do is pass a function which can get executed later. This can be done by wrapping the function in another function. So if you try this:
<TouchableWithoutFeedback
onPress={() => { this.setState({
text: 'Goodbye World',
}) }}>
It should behave as expected. All I did was wrap this.setState(...) with an arrow function so it becomes () => { this.setState(...) }
You can't use state with functional component. So try to use React Hooks.
Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.
https://en.reactjs.org/docs/hooks-intro.html
import React, {useState} from 'react';
...
const App = () => {
const [text, setText] = useState('Hello World');
...
<TouchableWithoutFeedback
onPress={() => setText('Bye-bye')}>
...

Trigger onPress event on Child component within Parent component

I made a button component which I render in my Parent component. Been stuck for a while now on trying to fire a function on the onPress event of this child component that is used in my Parent.
I've been looking through some of the recommended questions and answers but I need some specific advice.
I simplified my code as much as possible, please have a quick look.
Thanks in advance!
// PARENT COMPONENT
export class Home extends Component {
constructor(props) {
super(props);
this.onPress = this.onPress.bind(this);
}
onPress = () => {
console.log("Hey");
};
render() {
return (
<View style={styles.container}>
<PrimaryButton text={"Sign up"} onPress={this.onPress} />
</View>
);
}
}
// CHILD COMPONENT
const PrimaryButton = ({ text }) => {
return (
<TouchableOpacity style={style.container} >
<Text style={style.text}>{text}</Text>
</TouchableOpacity>
);
};
export default PrimaryButton;
You need to pass onPress to TouchableOpacity as a prop. I don't know the props for TouchableOpacity but should be bound as either onPress or onClick. Event handlers always need to be passed to the root component (or as close to it as you can get, ie TouchableOpacity is from a 3rd party). Most, if not all, 3rd party components will have props for the proper events.
// CHILD COMPONENT
const PrimaryButton = ({ text, onPress }) => {
return (
<TouchableOpacity style={style.container} onClick={onPress} >
<Text style={style.text}>{text}</Text>
</TouchableOpacity>
);
};

Why is component rendering before this.setState() has finished setting the state?

I am building my first react native app. I am using Firebase, Redux, and React Native to build my app.
I am using a thunk to fetch data from my database and I want to set my component's local state with that data.
When I console.log(this.props.room) in the render function I can see the data from my database but it's not being added to the local state in componentDidMount(), so I know my thunk and the backend is working properly.
I think my component is rendering before my local state is set in the componentDidMount function. Is there a way to prevent it from rendering while this.setState() is running? My code is below. Or is there another reason this.setState isn't working?
import { connect } from 'react-redux';
import {
Image,
Platform,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View,
TextInput
} from 'react-native';
import { submitIdea, getRoom } from '../redux/reducers/rooms/actions'
class RoomScreen extends React.Component {
static navigationOptions = {
title: 'Undecided!'
};
constructor(props) {
super(props)
this.state = {
currentUser: '',
submittedIdea: false,
currentUserIdea: '',
roomName: '',
ownerName: '',
peopleAndIdeas: [],
prompt: '',
room: {}
}
this.handleIdeaSubmit = this.handleIdeaSubmit.bind(this)
}
handleIdeaSubmit() {
const { user, roomName } = this.props.navigation.state.params
this.setState({ submittedIdea: true })
this.props.submitIdea(user, this.state.userIdea, roomName)
}
async componentDidMount() {
const { user, roomName } = this.props.navigation.state.params
await this.props.getRoom(roomName)
this.setState({
room: this.props.room
})
}
render() {
return (
<View style={styles.container}>
<ScrollView contentContainerStyle={styles.contentContainer}>
<View>
<Text>Room name: {this.state.room.name}</Text>
</View>
<View>
<Text>This room was created by: {this.state.room.owner}</Text>
</View>
<View>
<Text>What are we deciding? {this.state.room.prompt}</Text>
</View>
{/* checking whether or not the user has submitted an idea and altering the view */}
{!this.state.submittedIdea ?
<View style={styles.container}>
<TextInput
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
onChangeText={(text) => this.setState({ userIdea: text })}
value={this.state.createRoomName}
placeholder="Enter your idea."
/>
<TouchableOpacity
style={styles.button}
onPress={this.handleIdeaSubmit}
>
<Text> Submit Idea. </Text>
</TouchableOpacity>
</View> :
<View>
<Text>Your idea was: {this.props.userIdea}</Text>
</View>
}
<View style={styles.getStartedContainer}>
<Text>IDEAS</Text>
</View>
<View>
<Text style={styles.getStartedText}>USERS</Text>
</View>
</ScrollView>
</View >
);
}
}
const mapStateToProps = state => ({
room: state.room.room
})
const mapDispatchToProps = (dispatch) => ({
getRoom: (roomName) => dispatch(getRoom(roomName)),
submitIdea: (user, idea, roomName) => dispatch(submitIdea(user, idea, roomName))
})
export default connect(mapStateToProps, mapDispatchToProps)(RoomScreen)```
This is because setState actions are asynchronous and are batched for performance gains. This is explained in documentation of setState.
setState() does not immediately mutate this.state but creates a
pending state transition. Accessing this.state after calling this
method can potentially return the existing value. There is no
guarantee of synchronous operation of calls to setState and calls may
be batched for performance gains.
So you can do something like this,
this.setState({foo: 'bar'}, () => {
// Do something here.
});
async componentDidMount() {
const { user, roomName } = this.props.navigation.state.params
await this.props.getRoom(roomName)
this.setState({
room: this.props.room
})
}
You don't have to use local state in this case.
render() {
const {room: {name}} = this.props;
return (
<View style={styles.container}>
<ScrollView contentContainerStyle={styles.contentContainer}>
<View>
<Text>Room name: {name}</Text>
</View>
</ScrollView>
</View>
)
}
Here since you are using redux (global state), you don't have to add it in your local state. You can directly access the value of the room from the props.

"Error: this.setState is not a function" Trying to uncheck CheckBox in React Native?

I'm trying to implement React Native's CheckBox component but can't seem to get it unchecked after changing its value the first time- it's perpetually checked. I understand that the value prop must be set for it to reflect user actions, so I'm trying to setState onChange... what am I getting wrong? :/
export const ReviewCardCheckBox = (props) => {
this.state = { checked: false };
return (
<View style={styles.sectionStyle}>
<View style={{justifyContent: 'flex-start', flex:1}}>
<Text style={styles.fontStyle}>{props.option}</Text>
</View>
<View style={{flexDirection: 'row', justifyContent: 'flex-end', flex:1}}>
<CheckBox onChange={ () => this.setState( {checked: !this.state.checked} )}/>
</View>
</View>
)
};
Thanks for the help!
What you have is a stateless functional component. The purpose of these components is just to receive props and do something with them.
Learn more about the difference here
This function is a valid React component because it accepts a single “props” (which stands for properties) object argument with data and returns a React element. We call such components “functional” because they are literally JavaScript functions.
For you to have state you must do
class ReviewCardCheckBox extends React.Component {
constructor(props) {
super(props);
this.state = { checked: false };
}
render() {
// do what you want.
}
}
You are declaring a Stateless component (a function) so this Component can't have state, as well as it can't setState.
Declare the Component as class instead.
export class ReviewCardCheckBox extends React.Component{
constructor(props){
super(props);
this.state = {chacked: false};
}
render(){
return (
//do something here
);
}
}

Categories