Issue: Switch(toogle) component does not update its rendered view (on screen) unless i scroll through the cards/or i press on a card.
Code:
constructor() {
super();
this.state = {
outputimg:null,
displayedImages:[],
pageToken:null,
nrOfPhotosOnScreen:0,
pageNames:null,
authorOfThePost:null,
visible:false,
checked:false,
key:0
}
}
This for loop is inside a function that is called onComponentDidMount() but there is no problem with anything else except the switch -->
for(var k=0;k<=Object.keys(this.state.pageNames).length-1;k++){
if(firstPage.items[i].path.replace(/images\//g,'').includes(Object.keys(this.state.pageNames)[k]))
{
let authorOfThePost = Object.values(this.state.pageNames)[k];
let linkToPage = "https://www.instagram.com/"+authorOfThePost.replace(/@/g,'')+"/";
let headerPost = (props) => (
<View {...props}>
<Text category='h6'>Posted by </Text><Text style={{color:'blue'}} category='h6' onPress={() => Linking.openURL(linkToPage)}>{authorOfThePost}</Text>
</View>
)
let footerPost = (props) => (
<View {...props} style={[props.style, styles.footerContainer]}>
<Toggle checked={this.state.checked} key={this.state.key} onChange={this.onCheckedChange}>
{`Checked: ${this.state.checked}`}
</Toggle>
</View>
)
let output = <Card style={styles.card} activeOpacity={1} footer = {footerPost} header={headerPost}>{outputimg}</Card>
this.setState({
displayedImages: [...this.state.displayedImages, output],
key:this.state.key+1
});
}
onCheckedChange function -->
onCheckedChange = () => {
this.setState({checked:!this.state.checked})
};
The switch button and the card are imported from UI Kitten( Switch Toggle and Card Component)
| Package | Version |
| ----------- | ----------- |
| @eva-design/eva | ^2.0.0 |
| @ui-kitten/components | ^5.0.0 |
There are way too many issues with your code, including performance penalties, and stalled state variables.
The latter one is the issue you are experiencing. It is due to you saving components in state variables. Do not do that.
More specifically, in your case, the Toggle component uses this.state.checked, which at the time of setting the component in the state (with the this.setState(...) in your for loop in componentDidMount) still has the old value. Moreover, the component that you set in state, is only set at componentDidMount time, and it is not updated when you call the onCheckedChange method.
This is called "stalled state", which simply means that the most recent state is still not the most updated state.
The reason why it updates when you scroll over it is because something causes a re-render either in the Toggle component or your whole component, which causes it to re-read the this.state.checked variable - but stalled state issues usually cause undefined behaviors.
I will try to give you an example of better code to use, but keep in mind that this code is incomplete since I don't have your full code. You need to add missing code parts in the proper places.
class Post extends React.Component{
constructor(props) {
super(props);
this.state = {
outputimg: null,
displayedImages: [],
pageToken: null,
nrOfPhotosOnScreen: 0,
pageNames: null,
authorOfThePost: null,
visible: false,
checked: false,
key: 0
}
}
componentDidMount = () => {
// Do not loop through an array with a conditional for finding a single value
// The Array.find() method works great in this case
const pageNamesList = Object.keys(this.state.pageNames);
const pageName = pageNamesList.find((_pageName) => {
// Not sure where `firstPage` or `i` comes from
// it did not appear in the code you posted
// make sure you update it here as you need
return (firstPage.items[i].path.replace(/images\//g, '').includes(_pageName));
});
// If the Array.find() method cannot find a result, returns undefined
if (pageName !== undefined) {
// Now you have your page name here, evaluate values that you need
const authorOfThePost = this.state.pageNames[pageName];
const linkToPage = "https://www.instagram.com/" + authorOfThePost.replace(/@/g, '') + "/";
const data = { authorOfThePost, linkToPage };
this.setState({
displayedImages: [
...this.state.displayedImages,
data // append the newly evaluated data
]
});
}
}
onCheckedChange = () => {
// Try not to use the `!` (not) operator to toggle booleans
this.setState({
checked: (this.state.checked === false)
});
}
headerPost = (props) => (
<View {...props}>
<Text category='h6'>Posted by </Text><Text style={{color:'blue'}} category='h6' onPress={() => Linking.openURL(linkToPage)}>{authorOfThePost}</Text>
</View>
)
footerPost = (props) => (
<View {...props} style={[props.style, styles.footerContainer]}>
<Toggle checked={this.state.checked} onChange={this.onCheckedChange}>
{`Checked: ${this.state.checked}`}
</Toggle>
</View>
);
render = () => {
return this.state.displayedImages.map((data) => {
return <Card
// Do not save `key`s in state, and do not use `index` or counting as keys
// Any string value can be a valid `key`, just make sure it's unique
// The `linkToPage` string should be unique enough
key={`${data.linkToPage}`}
style={styles.card}
activeOpacity={1}
footer={this.footerPost}
header={this.headerPost}
>
{/* not sure where `outputimg` comes from */}
{outputimg}
</Card>;
});
}
}
Thank you so much, after an hour of modifying some of the information you provided I made it work!
Most helpful comment
There are way too many issues with your code, including performance penalties, and stalled state variables.
The latter one is the issue you are experiencing. It is due to you saving components in state variables. Do not do that.
More specifically, in your case, the Toggle component uses
this.state.checked, which at the time of setting the component in the state (with thethis.setState(...)in your for loop incomponentDidMount) still has the old value. Moreover, the component that you set in state, is only set atcomponentDidMounttime, and it is not updated when you call theonCheckedChangemethod.This is called "stalled state", which simply means that the most recent state is still not the most updated state.
The reason why it updates when you scroll over it is because something causes a re-render either in the Toggle component or your whole component, which causes it to re-read the
this.state.checkedvariable - but stalled state issues usually cause undefined behaviors.I will try to give you an example of better code to use, but keep in mind that this code is incomplete since I don't have your full code. You need to add missing code parts in the proper places.