I have a button and when I click on it, button changes its state. But I have to double tap to change the state. I console logged it and on first click, it shows blank and when I click on it again, it will change its state. Below is my code:
class CustomButtonOne extends React.Component {
constructor(props) {
super(props);
this.state = {
buttonOne:'',
};
this.selectionOnPressOne = this.selectionOnPressOne.bind(this),
}
selectionOnPressOne = () => {
this.setState({
buttonOne:'ONE'
})
console.log(this.state.buttonOne, 'this.state.buttonOne')
}
render() {
return (
<View>
<TouchableOpacity
onPress={()=> this.selectionOnPressOne()}>
<Text>Button</Text>
</TouchableOpacity>
</View>
)
}
}
export default CustomButtonOne
Why is this happening? and How can I change the state with one tap?
Any advice or comments would be appreciated thanks!
The setState action is async, therefore the changes that you log may not be available immediately.
Try this
this.setState({
buttonOne:'ONE'
}, () => console.log(this.state.buttonOne, 'this.state.buttonOne'))
Related
I am trying to update state then check the state with the 'addListener' in the 'componentDidMount'
When I update my state, I can see that my state is successfully updated but the addListener won't update.
I think I should see the console.log('state is the same') when I update my state but noting is happening.
Any Ideas?
This is my code:
export default class Update extends React.Component {
constructor(props) {
super(props)
this.state = {
number:1,
newNumber:undefined
}
console.log(this.state.number, 'number')
}
componentDidMount() {
this.props.navigation.addListener('willFocus', () => {
//when screen is loaded I get this console.
if(this.state.number != this.state.newNumber){
console.log('state is not the same')
}
//when I update state I should see this console log but nothing is happening.
if(this.state.number == this.state.newNumber){
console.log('state is the same')
}
})
}
render() {
return (
<View style={{flex:1, justifyContent:'center', alignItems:'center'}}>
<Button title='update' onPress={()=> this.setState({newNumber:1}, ()=> console.log(this.state.newNumber, 'newNumber'))}/>
</View>
);
}
}
Basically what you are trying to achieve is to check that onClick of button , there is a newState which is set , and you want to check with existing one , did it update correctly or not.
So you are using this.props.navigation.addListener('willFocus') which gets called when the screen is focused while coming from other screens. but in your case you are in the same screen and you update the state by calling the button, so it wont get called.
Rather react has its own lifecylce method called componentDidUpdate , and if you check the condition there for any state updates you will achieve it.
Please find below code and also expo link,
export default class Update extends React.Component {
constructor(props) {
super(props)
this.state = {
number:1,
newNumber:undefined
}
console.log(this.state.number, 'number')
}
componentDidUpdate(){
if(this.state.number != this.state.newNumber){
alert('state is not the same')
}
//when I update state I should see this console log but nothing is happening.
if(this.state.number == this.state.newNumber){
alert('state is the same')
}
}
render() {
return (
<View style={{flex:1, justifyContent:'center', alignItems:'center'}}>
<Button title='update' onPress={()=> this.setState({newNumber:1}, ()=> console.log(this.state.newNumber, 'newNumber'))}/>
</View>
);
}
}
expo link expo
hopeit helps, feel free for doubts
the reason why your code wont work is because you want to listen to state changes but your listener only listen to navigation changes which only triggered when the screen will focus.
to detect the changes to your state in class component, you can always use the lifecycle of react which is componentDidUpdate
what you can do perhaps something like this.
export default class Update extends React.Component {
constructor(props) {
super(props)
this.state = {
number:1,
newNumber: null
}
console.log(this.state.number, 'number')
}
componentDidMount() {
this.check()
}
componentDidUpdate(prevState) {
if(prevState.newNumber !== this.state.newNumber) {
this.check()
}
}
check = () => {
if(this.state.number != this.state.newNumber){
console.log('state is not the same')
} else {
console.log('state is the same')
}
}
render() {
return (
<View style={{flex:1, justifyContent:'center', alignItems:'center'}}>
<Button title='update' onPress={()=> this.setState({newNumber:1}, ()=> console.log(this.state.newNumber, 'newNumber'))}/>
</View>
);
}
}
I have created a custom component which takes a color name from the parent component and updates that color in the state in the parent component. Currently, after I have done all the code, it does not save the new color, and therefore, does not update the the state.
This is for a react-native android app that I am building. I have looked at the ReactNative documentation for flatlist and textinput. I have looked at Stack overflow for solutions too
Set up a react native project. this is my parent component
class HomePage extends Component {
constructor(props) {
super(props);
this.state = {
backgroundColor: "blue",
availableColors: [
{name: 'red'}
]
}
this.changeColor = this.changeColor.bind(this)
this.newColor = this.newColor.bind(this)
}
changeColor(backgroundColor){
this.setState({
backgroundColor,
})
}
newColor(color){
const availableColors = [
...this.state.availableColors,
color
]
this.setState({
availableColors
})
}
renderHeader = ()=>{
return(
<ColorForm onNewColor={this.newColor} />
)
}
render() {
const { container, row, sample, text, button } = style
const { backgroundColor, availableColors } = this.state
return (
<View style={[container,{backgroundColor}, {flex: 1}]} >
<FlatList
data={availableColors}
renderItem={
({item}) =>
<ColorButton
backgroundColor={item.name}
onSelect={(color)=>{this.changeColor(color)}}>
{item.name}
</ColorButton>}
keyExtractor={(item, index) => index.toString()}
ListHeaderComponent={this.renderHeader}
>
</FlatList>
</View>
);
}
}
this is the code for ColorForm component
class ColorForm extends Component {
constructor(props) {
super(props);
this.state = {
txtColor:'',
}
this.submit = this.submit.bind(this)
}
submit() {
this.props.onNewColor(this.state.txtColor.toLowerCase())
this.setState({
txtColor: 'yellow',
})
}
render() {
const {container, txtInput, button} = style
return (
<View style={container}>
<TextInput style={txtInput}
placeholder="Enter a color"
onChangeText={(txtColor)=>this.setState({txtColor})}
value={this.state.txtColor}></TextInput>
<Text
style={button}
onPress={this.submit}>Add</Text>
</View> );
}
}
and below is the code for ColorButton component
export default ({backgroundColor, onSelect=f=>f}) => {
const {button, row, sample, text} = style
return (
<TouchableHighlight onPress={()=>{onSelect(backgroundColor)}} underlayColor="orange" style={button}>
<View style={row}>
<View style={[sample,{backgroundColor}]}></View>
<Text style={text}>{backgroundColor}</Text>
</View>
</TouchableHighlight>
)
}
The imports and stylesheets are setup as standard and do not effect the code so I have chosen to not show them.
EDIT: Adding the expo snack here.
Expected Behavior:
When I press "ADD" on the ColorForm component, it should take that color and add that to the this.state.availableColor array and therefore visible in the ColorButton component. And when I touch the button, it should make that change
Current behaviour:
When I enter a color and press on add, it makes an empty button in the ColorButton component - NOT the color i entered in the color I entered in the ColorForm component.
EDIT: Adding the expo snack here.
Your state is updating but the FlatList is not updating. Because your data={availableColors} in flatlist is not changing but your state is changing .
Try to add extraData
A marker property for telling the list to re-render (since it implements PureComponent). If any of your renderItem, Header, Footer, etc. functions depend on anything outside of the data prop, stick it here and treat it immutably.
Try this
<FlatList
extraData={this.state.backgroundColor}
Updated Answer
the problem is in this function newColor(color)
const availableColors = [
...this.state.availableColors,
color
]
you just receive a string of color but you have defined object like this {name: 'red'}
please use this code
newColor(color){
const availableColors = [
...this.state.availableColors,
{name: color}
]
this.setState({
availableColors
})
}
Snack link with example : https://snack.expo.io/#mehran.khan/healthy-cake-state-sample
Also add export default to main component to remove error of launch
export default class HomePage extends Component {
App Preview
I had many problems using setState() in the way you are using now. I recommend you to use in this way setState(), with a callback:
this.setState((previousState, currentProps) => {
return { ...previousState, foo: currentProps.bar };
});
This is one of the article that talks about it.
setState() does not always immediately update the component. It may
batch or defer the update until later.
From react website setState().
I will try to explain my case:
I have 2 pages/screens
SreenA.js :
...
<FlatList>
</FlatList>
...
ScreenB.js :
...
<Button> This button for back to ScreenA </Button>
...
What i want to achieve :
When i press back button in screenB , my app will back to screenA.
When i come back to screenA , i need to refresh the FlatList. (each
time i back to screenA).
How i achieve that case in Android Native ?
I add this section to help you to more understand what challenge i
face. In Android Native, i will use onResume to refresh the list
(onResume called everytime the screen/page called).
What i already try but not work for me now :
I already try this but what i get it just called when app in background and then show in foreground :
state = {
appState: AppState.currentState
}
componentDidMount() {
AppState.addEventListener('change', this._handleAppStateChange);
}
componentWillUnmount() {
AppState.removeEventListener('change', this._handleAppStateChange);
}
_handleAppStateChange = (nextAppState) => {
if (this.state.appState.match(/inactive|background/) && nextAppState === 'active') {
console.log('App has come to the foreground!')
//i place the code here for refresh the flatList. But not work
}
this.setState({appState: nextAppState});
}
Add navigation.addListener to fire every time a page receives navigation focus.
As an example:
class Page1 extends React.Component {
constructor(props) {
super(props);
this.state = { ts: 0 }
// this will fire every time Page 1 receives navigation focus
this.props.navigation.addListener('willFocus', () => {
this.setState({ ts: Date.now() })
})
}
render() {
return (
<View style={styles.container}>
<Text style={styles.paragraph}>
Page 1 [{this.state.ts}]
</Text>
<Button title="Page 2" onPress={() => this.props.navigation.navigate('Page2')}/>
</View>
);
}
}
For the full example, check the following snack on your phone. The timestamp will update every time you navigate back to page 1 but not when the app resumes.
You should be able to expand it for your needs from this example.
I'm having an issue getting my child component to update with new props. I'm deleting an item from the global state and it's successful. But when the item gets deleted, I'm expecting that item to no longer show. It's still being shown but if I were to move to another screen then back, it's gone. Any idea on what I might be missing here?
Thanks!!
export default class Summary extends Component {
constructor(props) {
super(props);
this.state = {
pickupData: this.props.pickup
};
}
handleDelete(item) {
this.props.deleteLocationItem(item);
}
render() {
const pickup = this.state.pickup;
return (
<View>
{pickup.map((item, i) => (
<LocationItem
name={item}
onPressDelete={() => this.handleDelete(item)}
/>
))}
</View>
);
}
}
const LocationItem = ({ onPressDelete, name }) => (
<TouchableOpacity onPress={onPressDelete}>
<Text>Hi, {name}, CLICK ME TO DELETE</Text>
</TouchableOpacity>
);
------- Additional Info ------
case 'DELETE_LOCATION_INFO':
return Object.assign({}, state, {
pickup: state.pickup.filter(item => item !== action.action)
})
export function deleteLocationInfo(x){
return {
type: DELETE_LOCATION_INFO,
action: x
}
}
Your deleteLocationItem must be something like this:
deleteLocationItem(id) {
this.setState({
items: this.state.items.filter(item => item.id !== id)
});
}
Then inside your Summary class you dont need to set the prop again. Just receive pickup from props like this:
render (
const { pickup } = this.props;
return(
<View>
{ pickup.map
...
Render is happening based on the state which is not updated other than in constructor. When the prop updates from parent, it is not reflected in the state.
Add componentWillReceiveProps method to receive new props and update state, which will cause new data to render
But more preferably, if the state is not being changed in any way after initialization, render directly using the prop itself which will resolve this issue
I have a button on the homescreen which toggles the text in the AlertBar.
So when I press a Button, the text in AlertBar should change according to the state isParked. Currently when I press the button, nothing happens... and I'm unsure why.
Here's my homescreen:
class Home extends Component {
constructor(props) {
super(props);
this.state = {
isParked: false
};
}
pressPark = () => this.setState({isParked:true})
render() {
console.ignoredYellowBox = ['Remote debugger'];
return (
<View>
<View>
<AlertBar isParked={this.state.isParked}/>
</View>
<View style={styles.parkButton}>
<Button title='PARK' onPress={this.pressPark} color='green'/>
</View>
</View>
);
}
}
Here's my AlertBar.js:
class AlertBar extends Component {
state = {
region: 'Singapore',
isParked: this.props.isParked,
alertText: null
}
... some unrelated code ...
componentDidMount() {
if (this.state.isParked === false) {
this.setState({alertText: "You're parking at"})} else if (this.state.isParked === true) {
this.setState({alertText: "You're parked at"})}
alert(this.state.alertText)
}
componentWillUnmount() {
// some unrelated code
}
render() {
... some unrelated code...
return(
<View style={styles.container}>
<Text style={styles.welcomeText}>
{this.state.alertText}
</Text>
<Text style={styles.locationText}>
{this.state.region}
</Text>
</View>
)
}
}
Am I doing this wrong? I can't tell what's wrong.... Please help! Thanks!
Use
if (this.props.isParked === false)
Instead of
if (this.state.isParked === false)
(and dont transfer props to state directly, this make no sense anyway :))
At this point, your AlertBar component is not handling any prop changes.
What you'll need to do, is map your props to the state whenever an update is received.
Add this line of code in your AlertBar.js and it will map isParked to state whenever it receives an update.
componentWillReceiveProps(props) {
this.setState({ isParked: props.isParked });
}