Check if navigation state params object has been set - react-native

I'm trying to do some validation to see if the params object has been set when navigating to my GetPromoScreen but it does not work. Here is my sample code:
HomeScreen (navigating to GetPromoScreen)
this.props.navigation.navigate('GetPromoScreen', { promo: true })
GetPromoScreen (validating the params object)
if(this.props.navigation.state.params.promo != undefined){
this.setState({ promo: true })
} else {
this.setState({ promo: false })
}
I have an alert dialog from HomeScreen where when the user press the button, it will trigger the function where I set the params promo. But, If the user closes the alert dialog and straight away entering GetPromoScreen from the menu button, the params promo will not be set and when that happen, an error will appear saying:
TypeError: undefined is not an object (evaluating
'this.props.navigation.state.params.promo')
My validation code above is not working. Any idea?

if(this.props.route.params){
formData.push('profile_id' + "=" + this.props.route.params.profile_id);
}

Related

An element that has v-if isn't updated as expected

Lately I've been trying to use VueJS to create a small project and I have encountered this problem. So I have this navigation bar that includes a Log Out Button element that'd only shown if someone has already logged in. This is my Navbar before I logged in.
Before Login Navbar. No Log Out Button
After I logged in, my navbar'd be like this.
after logged in Navbar. Log Out Button exists
This is my code for this navbar component.
<div id="logout-button" v-if="isLoggedIn">
<button v-on:click="logOut">Log Out</button>
</div>
....
<script>
export default {
name: "NavigationBar",
data: function () {
return {
isLoggedIn: false,
};
},
mounted() {
if (localStorage.isLoggedIn !== undefined) {
this.isLoggedIn = localStorage.isLoggedIn;
}
},
methods: {
...
logOut: function () {
localStorage.isLoggedIn = false;
localStorage.access = null;
localStorage.refresh = null;
location.reload();
},
},
};
</script>
What I've been trying is whenever the log out button clicked, I'd change the value of a key in my local storage, named "isLoggedIn" from "true" to "false". After that, I'd reload the page and when the page reached mounted, the component's data named "isLoggedIn"'d be changed with the new value of my local storage which is now "false". Hence, my expectation is the button wouldnt' be there.
The problem is, whenever I clicked log out, the Log Out Button'd be always there as if I didn't change the value that hopefully doesn't evaluated as "true" in the v-if. I don't know which or what is the problem since I'm new to Vue and I really hope you guys could tell me what it is. Thank you very much!
In your logOut() method, you're only changing the value in the localStorage, however the v-if="isLoggedin" is binded to the component's data so you also need to update that.
logOut: function () {
localStorage.isLoggedIn = false;
localStorage.access = null;
localStorage.refresh = null;
this.isLoggedIn = false
location.reload();
},
Additionally, you can only store strings in the localStorage, so you need to evaluate your string to return a boolean for your component's data.
this.isLoggedIn = (localStorage.isLoggedIn === 'true')
Here is a small jsfiddle for you to play with: https://jsfiddle.net/6rv7j5bg/
Try using:
localStorage.setItem("isLoggedIn", false)
and:
localStorage.getItem("isLoggedIn")
and see if it makes any difference!

How do I call functions inside render() method and still keep it pure?

I want to use a modal in my React Native app that ask the user to confirm his action.
The state looks like this:
state = {
dialogVisible: false,
confirmed: null
}
If the user confirms his delete action (turning confirmed to true), I want to execute my delete() method.
Delete method:
delete = () => {
const { deckName } = this.props.navigation.state.params
console.log('WAS CONFIRMED')
this.setState({
dialogVisible: false
})
this.props.navigation.navigate('Decks')
removeDeckFromStorage(deckName)
this.props.dispatch(removeDeck(deckName))
this.setState({
confirmed: null
})
}
noDelete = () => {
this.setState({
dialogVisible: false
})
this.setState({
confirmed: null
})
}
When the user confirmed his action, the modal closes, and the delete is done. Afterwards, I want to set confirmed back to null re-use it later.
On the other hand, if the user does not confirm the modal by clicking No, the noDelete() method should be called, which just closes the modal and sets confirmed back to null.
My problem is now that I get a warning saying:
Warning: Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state.
That is because I check for the state of confirmed inside the render() method:
const { confirmed } = this.state
if (confirmed){
this.delete()
}
else if (confirmed === false){
this.noDelete()
}
I did this because when I checked the state of confirmed inside the delete method right after setting the confirmed state to true, it always said null.
I put the check inside render because after the confirmed state is changed through the user input, the component is re-rendered and thus giving me the right state for the query.
The dialog buttons each change the current confirmed state when clicked:
positiveButton={{
title: "YES",
onPress: () => this.setState({confirmed: true})
}}
So, how can I check for confirmed after it was set but still outside of the render method to keep it pure?
You should never update the state in the render method. You should move your logic to the delete and noDelete functions instead.
I'm not sure how your modal is, however let's suppose it's something like this:
<View>
<TouchableOpacity onPress={this.delete}>
<Text>Delete</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.noDelete}>
<Text>No Delete</Text>
</TouchableOpacity>
</View>
And in the delete and noDelete you simply remove the setState({ confirmed }) since you're already calling the deletion from there.

React Native Multiselect

I am new to React Native
I am trying to create a multiselect view where user can select and deselect the items and then the selected items should pass back to the previous container and when user comes back to the next view the selected items should be checked.
I am trying to implement but getting the issue it is not updating data accurately. It shows only 1 selected item when I came back again to the screen.
Can anyone tell me the best way to do that or if there is any tutorial.
Should I do it with Redux or using react native?
Any help would be appreciated!!
Thanks!!
I believe the issue you describe is due to the following:
In componentDidMount you are calling updateItemWithSelected in a loop. This updateItemWithSelected call is both overwriting the checked attributes for all of the arrayHolder values on each call and also not using the updater function version of setState, so the later call of the loop may overwrite the earlier calls since setState is async and batched. If you are not using updateItemWithSelected elsewhere you should simplify componentDidMount to:
componentDidMount() {
const selectedTitles = {};
const { state } = this.props.navigation
const params = state.params || {};
if (params.data.length){
params.data.forEach((element) => {
// create a map of selected titles
selectedTitles[element.title] = true;
})
}
const arrayHolder = this.array.map(item => {
// map over `this.array` and set `checked` if title is in `selectedTitles`
return {...item, checked: !!selectedTitles[item.title]};
});
this.setState({ arrayHolder });
}
and delete updateItemWithSelected.

How to store route in react-native with react-navigation?

I'm trying to make react-navigation remember user path.
So, it's saved where users are in redux.
This is for when the user clicks a back button, and pop the screen with LIFO way.
I wonder if what I'm trying to do is the correct way.
The problem is that my way causes when the touchable thing is clicked multiple times.
If it clicked multiple times, that method which is related to stack the path where the user is is called multiple times and stacked the path multiple times in the redux array.
Here's my code.
clicked() method makes clicked state to true.
init_clicked() method makes clicked state to false.
mainScreen.js
/*
if click button then call moveToFavorite method.
*/
moveToFavorite = async () => {
const {fetching, ScreenActions, navigation, clicked} = this.props;
const {push, navigate} = navigation;
/*currunt state
{
userPath: [],
clicked: false
}
*/
if(!clicked) {
ScreenActions.clicked();
await ScreenActions.stackScreen('favorite'); // save the path name, 'favorite' to redux
/* state
{
userPath: ['favorite'],
clicked: false <= but what I expect was it should be 'true'
so it can make clicking multiple times.
if click twice then, userPath is ['favorite', 'favorite']
}
*/
navigate('FavoriteScreen');
}
}
favoriteScreen.js
componentDidMount() {
const {navigation, ScreenActions} = this.props;
ScreenActions.init_clicked(); // this one executes earlier than the ScreenActions.clicked() in favoriteScreen.js
//That's why state clicked state is changed to false right away.
}

How do I reset nextProps.arbitraryValue once I have used it?

I am working on a react-native project.
I have Component A that calls Component B.
When screen B is finished doing work, it calls:
NavigationActions.pop({refresh: {workComplete: true}})
And on Screen A, I have the following code:
componentWillReceiveProps(nextProps) {
if (nextProps.workComplete) {
window.alert('work was completed');
}
}
However, the props.workComplete stays set, and I'm not sure how to unset it, so I keep getting an alert when props are changed in this component.
How can I reset that property value?
Not sure what you mean by unset, but this will be called everytime props change and since workComplete is set to true, it will constantly alert you. You can have workComplete as a state value and do something like this:
componentWillReceiveProps(nextProps) {
if (nextProps.workComplete !== this.state.workComplete) {
this.setState({ workComplete: true }, () => window.alert('work was completed'));
}
}