How can I set a state only if there is a condition? - react-native

I have a percentage into a text view that changes, but it can't be more than 100%. I use a function with a setstate and I want that if the percentage in the text view is more than 100% it changes in 100%.
Something like this:
onPressLearnMore= () => {
this.setState({
rareperc1: (this.state.rareperc2*this.state.boxes),
rareperc:parseFloat(this.state.rareperc1).toFixed(4),
if(rareperc==100){
rareperc:100,
}
...
});
}
The actual code is:
onPressLearnMore= () => {
this.setState({
rareperc1: (this.state.rareperc2*this.state.boxes)
rareperc:parseFloat(this.state.rareperc1).toFixed(4),
...
});
}

You can do like this.
rareperc:this.state.rareperc1>100?100:parseFloat(this.state.rareperc1).toFixed(4)

I solved with this:
onPressLearnMore= () => {
var rarepervalue = (this.state.rareperc2*this.state.boxes)+
(this.state.boxes*this.state.incremento1);
if(rarepervalue>100) rarepervalue=100;
this.setState({
percentages: percentuale,
rareperc1: rarepervalue,
rareperc:parseFloat(this.state.rareperc1).toFixed(4),

Related

Value from dynamic table (Cypress)

I need to take a value from a table that changes after every page refresh. For example CPU for Chrome
describe('test dynamic table and check Chrome CPU', () => {
it('passes', () => {
cy.visit('some url')
cy.contains('span', 'CPU')
.parent()
.within(() => {
cy.contains('[role=cell]', 'Chrome')
})
})
})
Table:
UPDATE:
FYI
After refreshing the page, you will get columns and row changing place:
enter image description here
You can do like this:
cy.contains('span', 'Chrome')
.parent()
.within(() => {
cy.contains('[role=cell]', '%')
.invoke('text')
.then((text) => {
cy.log(text) //Logs 8.3%
//removing % and converting into number and asserting
expect(+text.replace(/%/g, '')).to.be.greaterThan(5)
expect(+text.replace(/%/g, '')).to.be.lessThan(15)
expect(+text.replace(/%/g, '')).to.eq(8.3)
})
})

React native reanimated runOnJs - does not update state every time

I have a list of items that should change state when they are swiped passed a certain threshold. I'm using runOnJs to call a function to change the state. Now when I swipe an item the first time, it updates it's state but every swipe after that does nothing. Can someone please explain to me what I'm missing here?
let [cleaned, setCleaned] = useState(false);
let handleCleanPress = () => {
console.log(clean);
setCleaned(!cleaned);
translateX.value = withTiming(0);
};
let panGesture = useAnimatedGestureHandler<PanGestureHandlerGestureEvent>({
onStart: (_, context) => {
context.startX = translateX.value;
},
onActive: (event, context) => {
let start = context.startX + event.translationX;
if (start < 0) {
translateX.value = start;
}
},
onEnd: () => {
let shouldTriggerClean = translateX.value < translateXThreshold;
translateX.value =
translateX.value >= snapThreshold && translateX.value < -BUTTON_WIDTH
? withTiming(snapPoint, { duration: 200 })
: withTiming(0, { duration: 200 });
if (shouldTriggerClean) {
runOnJS(handleCleanPress)();
}
},
});
Feels a bit wrong doing it like this but it works. Maybe someone can suggest a better way or confirm this is correct?
let setCleanState = () => {
setCleaned(!cleaned);
};
let handleCleanPress = () => {
translateX.value = withTiming(0, { duration: 200 }, (finished) => {
if (finished) {
runOnJS(setCleanState)();
}
});
};
I think part of the problem here may be that you're mixing the "JS in UI Thread"("worklets", translateX.value) with the "Main React Native JS Thread"(setState).
Read more about that [here][1].
You fixed that in your follow-up comment by only using runOnJS on setCleanState. Which I think is why it was working, albeit not reliably.
Did you also remove the withTiming functions in your onEnd() after your comment?
[1]: https://docs.swmansion.com/react-native-reanimated/docs/#:~:text=interactions%20and%20animations,the%20UI%20thread).

setting state when using a .map function

I think I may running into a callback hell type scenario or this may be an issue with scope. I want setState with allLeagues array once the .map function is finished running. Problem is, when the .map function is done and this.setState({leagueAL: allLeagues)} is ran, the state is set to an empty array. I do not want to run this.setState inside the .map and set it multiple times. Any thoughts on how I can make the data persist into the state?
getLeagueMatches = () => {
let allLeagues = []
if(this.state.leagueMatchesInfo != null){
this.state.leagueMatchesInfo.map((league, id) => {
let leagueID = league.league_id;
fetch(`https://apifootball.com/api/?action=get_events&from=2017-09-11&to=2017-09-11&league_id=${leagueID}&APIkey=<APIKey>`)
.then(res => res.json())
.then(event => {
//console.log(event)
if(event.error){
//console.log(event.error)
}else{
league.matches = true;
//console.log(league)
allLeagues = [...allLeagues, league]
}
})
console.log(allLeagues)
})
this.setState({
leagueAL: allLeagues
})
}//end if(65)
};//end renderItem
.map() returns a new array. You'll need to catch it in a variable. In map, you should have a function that does something on each element.
allLeagues = this.state.leagueMatchesInfo.map((league, id) => {
...
}
this.setState({
leagueAL: allLeagues
})
Then setState after the map.
The issue is that you're not updating the allLeagues array until the promises have resolved. However, setState is being called before any of this even happens.
I would look into something like Promise.all(). Then you can create an array of promises with each call to fetch. Then you can call .then off your Promise.all() and set the state within the .then.

Adding and removing items in AsyncStorage

Looking to add some items, display a list then be able to delete each item separately, my understanding of this problem is that I have to create an array then update it each time I want to delete an item then save. That seems quite complex for a basic db operation, I tried several approachs don't know if my code is what I'm supposed to do. Can someone please point to the right direction?
The last error I'm facing:
Object is null or undefined from
C:\Users\oled\stock\node_modules\react-native\Libraries\polyfills\Array.es6.js:24:26
_toConsumableArray
FetchValue = () => {
AsyncStorage.getItem("Favorites").then((value) => {
this.setState({
favs: JSON.parse(value)
});
}).done();
};
SaveValue = () => {
const newFavs = [...this.state.favs, this.state.UserInput];
this.setState({ favs: newFavs, UserInput: '' }, () => {
AsyncStorage.setItem("Favorites", JSON.stringify(this.state.favs));
Keyboard.dismiss()
});
};
RemoveValue(item){
const index = this.state.favs.indexOf(item);
const newArray = [...this.state.favs];
newArray.splice(index,1);
this.setState({ favs: newArray });
AsyncStorage.setItem("Favorites", JSON.stringify(newArray));
}
Full code : https://pastebin.com/p7sbQTNG
I think after removing last element, your array is empty. So after that whenever you try to remove the value from array it will give you the error. So please check the condition where array count is greater than 0 or not.
I hope it will work for you.
replace your RemoveValue method with this code.
We need to check if the item is not null/undefined
RemoveValue(item){
if(item !== null && item !== undefined){
const index = this.state.favs.indexOf(item);
const newArray = [...this.state.favs];
newArray.splice(index,1);
this.setState({ favs: newArray });
AsyncStorage.setItem("Favorites", JSON.stringify(newArray));
}
this will resolve your crash.

React Native ListView - rowHasChanged doesn't fire

I am trying to implement an infinite scroll in React Native. Below is the source of the component:
var React = require('react-native');
var server = require('../server');
var Post = require('./Post');
var SwipeRefreshLayoutAndroid = require('./SwipeRefreshLayout');
var backEvent = null;
var lastPostId = "";
var isLoadingMore = false;
var isLoadingTop = false;
var onEndReachedActive = false;
var {
StyleSheet,
ListView,
View,
Text,
Image,
ProgressBarAndroid,
BackAndroid
} = React;
class Stream extends React.Component {
constructor(props) {
super(props);
this.ds = new ListView.DataSource({
rowHasChanged: (row1, row2) => {
console.log("rowHasChenged FIRED!!");
return false;
}
});
this.state = {
dataSource: this.ds.cloneWithRows(['loader']),
hasStream: false,
posts: []
};
}
componentDidMount() {
BackAndroid.addEventListener('hardwareBackPress', () => {
this.props.navigator.jumpBack();
return true;
}.bind(this));
server.getStream('', '', 15).then((res) => {
lastPostId = res[res.length-1].m._id;
this.setState({
posts: res,
hasStream: true,
dataSource: this.ds.cloneWithRows(res)
}, () => onEndReachedActive = true);
})
}
onRefresh() {
var posts = this.state.posts;
var firstPost = posts[0].m._id;
console.log(this.state.dataSource._rowHasChanged);
isLoadingTop = true;
server.getStream('', firstPost, 4000)
.then(res => {
console.log(posts.length);
posts = res.concat(posts);
console.log(posts.length);
this.setState({
dataSource: this.ds.cloneWithRows(posts),
posts
}, () => {
this.swipeRefreshLayout && this.swipeRefreshLayout.finishRefresh();
isLoadingTop = false;
});
}).catch((err) => {
isLoadingTop = false;
})
}
onEndReached(event) {
if(!onEndReachedActive) return;
if(this.state.loadingMore || this.state.isLoadingTop)return;
isLoadingMore = true;
var posts = this.state.posts;
server.getStream(posts[posts.length-1].m._id, '', 15)
.then(res => {
console.log('received posts');
posts = posts.concat(res);
lastPostId = posts[posts.length-1].m._id;
this.setState({
dataSource: this.ds.cloneWithRows(posts),
posts
}, ()=>isLoadingMore = false);
})
}
renderHeader() {
return (
<View style={styles.header}>
<Text style={styles.headerText}>Header</Text>
</View>
)
}
renderRow(post) {
if(post === 'loader') {
return (
<ProgressBarAndroid
styleAttr="Large"
style={styles.spinnerBottom}/>
)
}
let hasLoader = post.m._id === lastPostId;
let loader = hasLoader ?
<ProgressBarAndroid
styleAttr="Large"
style={styles.spinnerBottom}/> : null;
return (
<View>
<Post
post={post}/>
{loader}
</View>
)
}
render() {
return (
<ListView
style={styles.mainContainer}
dataSource={this.state.dataSource}
renderRow={this.renderRow.bind(this)}
onEndReached={this.onEndReached.bind(this)}
onEndReachedThreshold={1}
pageSize={15} />
);
}
}
The problem is that whenever I append (or prepend) new data, the rowHasChanged method of the DataSource doesn't fire. It just re-renders every row, even tho nothing has changed (except the new data).
Any idea why the method is bypassed?
Edit: Pass a function to setState to avoid race conditions
I just figured it out. If you are having the same issue, check the point at which you change your state with the new dataSource. Mine was like this:
this.setState({
dataSource: this.ds.cloneWithRows(posts)
});
Instead you should always use the dataSource from the previous state, like this:
this.setState(state => ({
dataSource: state.dataSource.cloneWithRows(posts)
}))
Cheers!
this worked for me, hope this helps. I created a new dataSource and assigned the updated data to it on state change as follows:`
var dataSource = new ListView.DataSource(
{rowHasChanged: (r1, r2) => ( r1 !== r2)});
this.setState({ dataSource : dataSource.cloneWithRows(posts) });
Now, the new data is assigned and the view is rendered correctly. Note that posts array that is assigned now holds the updated data. Still wondering though if it's the best way to do it but it works!
I agree it seems to make sense that you should always use the dataSource from the previous state.
Yet when I setState this way, rowHasChanged gets called for all rows, however, rowHasChanged always returns false and no rows are rendered??? Why?
// This is callback handler that the ListView DetailView will
// call when a ListView item is edited
onChange(waypoint: Object){
console.log('Callback: rowNumber= ', waypoint.rowNumber);
console.log(' length(m)= ', waypoint.distance.meters);
var itemListChanged = this.state.itemList;
itemListChanged[waypoint.rowNumber-1] = waypoint;
this.setState({
dataSource: this.state.dataSource.cloneWithRows(itemListChanged),
});
},
If I setState this way, renderRow is called for all rows unconditionally without rowHasChanged ever being called. Which is correct?
this.setState({
dataSource: ds.cloneWithRows(itemListChanged),
});
ListView, datasource, and react-native are a hard learning curve coming from C#/C/C++.
for anyone still having issue with rowHasChanged called but are still returning false the following snippets might help
the datasource is initialized like usual:
let ds = new ListView.DataSource ({
rowHasChanged: (a, b) => {
const changed = (a !== b)
return changed
}
})
this.data = []
this.state = {
listDataSource: ds.cloneWithRows(this.data)
}
here is the function which will update a row
updateRow = (row, rowId, sectionId) => {
// make a shallow clone from the stored data array
let blob = this.data.concat()
// modify the row, since we are using the triple equal operator, we need to make sure we are giving it a new object (new ref)
blob[rowId] = Object.assign({}, blob[rowId], {label: blob[rowId].label + '..cape..deh'})
// tell react to update the source
this.setState({
listDataSource: this.state.listDataSource.cloneWithRows(blob)
}, () => {
// we need to update our data storage here! after the state is changed
this.data = blob
})
}