For routing I use react-navigation.
There are two pages :
HomePage (main page).
HomePageList(main page only all items in list view)
Need to do so:
When I click on an item from the list on the HomePageList page. Must pass a value in state to the HomePage page.
Let's say I click on the 12 element on the HomePageList page.
On Numerate to state activeSlide should be passed 12.
There is an array of 6 elements there.
On page - HomePage. Slider with pagination . The Index of the object is stored in state. In the activeslide value.
Visit HomePageList all the same 6 element.Only list and search.
When I click on any element of the HomePageList. Occurs _GoHome.
_GoHome = async ( ) => {
this.props.navigation.navigate(HOMEPAGE);
};
As I pass through the function of another item.index in state HomePage.
I tried to do it this way:
To make a sort of constant ActiveSlide in a separate file which you want to save the item.index.
Then in HomePage to do so:
if (ActiveSlide !== null){
this.setState({
activeSlide: ActiveSlide
})
}else{
this.setState({
activeSlide: 0
})
}
But for some reason doesn't work says:
Error: ActiveSlide read only
With react navigation you can pass parameters in navigate()
On the view that wants to pass the parameter:
this.props.navigation.navigate(HOMEPAGE, { something: value })
On the view that wants to receive the paramter:
value = this.props.navigation.getParam('something')
See the docs here
Related
I am trying to create a button that changes the text when clicked and it saves a value to an array. I then need to save the state of the button and the array to the device using AsyncStorage.
I have the first part done (please see Snack link here) but I am struggling to get the AsyncStorage to work properly. If the button had been clicked once, I would expect that the array has the "item 1" value in it and the button would say clicked. Even if the app is closed and reopened, those values should still remain until the button is clicked again.
I have not found any solutions so far. Is there anyone that has some ideas?
This is the workflow that you should follow:
create an object in state (in this.state = {} for classes, or setState() with hooks) for your button's text value
initialize the above value with the value from AsyncStorage (make sure to add some conditional that if it's empty it returns [])
also take note of how Asyncstorage is async, meaning you'll have to add 'await' when you're assigning the value
add some conditional for the text value of your button, whereas it will show a loading icon, (or nothing, doesn't matter) while AsyncStorage is retrieving the initial data
onPress on your button will change the state value AND the AsyncStorage value (or you can only update the AsyncStorage value when you're closing the page with componentWillUnMount, or useEffect(() => return(--do it here--))
If you're using functional components, it would look something like this:
const [textValues, setTextValues] = useState([])
const setInitialValues = async () => {
const info = await AsyncStorage.getItem('myValues')
setTextValues(info)
}
useEffect(() => {
setInitialValues()
return(
AsyncStorage.setItem('myValues', textValues)
)
}, [])
return(
<View>
<Button onPress={() => setTextValues(textValues + 1) title={textValues.length === 0 ? '' : textValues[textValues.length - 1]}}
</View>
)
Most of the detail is in the title question. I'm not exactly struggling to navigate between screens, but I'm wondering if push/pop (or something else) can be used to step through a stack without identifying specific routes. So if I have a stack with a bunch of screens, can they have a button with something to the effect of
<TouchableOpacity onPress={() => navigation.push()}>
<Text>Next</Text>
</TouchableOpacity>
Or is this a complete misuse of push? The object here is to be able to change the order of the stack quickly in development without having to go through all of the screens and adjust the button paths. So if the screens are simply named 1, 2, 3, and 4 and I want to move 2 to the end (3 becomes 2, 4 becomes 3), I have to update the next/back button routes for screens 2, 3, and 4. Whereas if they are all coded as "next" or "back", all I do is cut and paste screen 2 after screen 4 in the stack navigator definition. Thanks for your help.
You could get the list of names, find the next name, and then push that. Define a helper like this:
const pushNext = () => state => {
// Get the current route name
const currentRouteName = state.routes[state.index].name;
// Get the index of the current route in the route name list
const currentRouteIndex = state.routeNames.indexOf(currentRouteName);
// Get the next item in the route name list
const nextRouteName = state.routeNames[currentRouteIndex + 1];
if (nextRouteName) {
// If there was a next route, return a push action
return StackActions.push(nextRouteName);
} else {
// Reset to current state for noop or whatever you want
return CommonActions.reset(state);
}
};
Then use it in your components:
navigation.dispatch(pushNext());
So, I have a specific problem where i need to initialize a state with custom values that comes from the parent component props.theValue. But the problem is that the props.theValue isn't ready when we set the state. So this line should never work:
const [grams, setGrams] = useState(props.theValue)
The scenario:
We have a list of food on the Parent Screen, when the users Clicks on it, it opens a modal (Like a PopUp) with some options to choose before the selected item gets manipulated
What I tried?
Set initial flag useState(-1) to indicate it should set the value from props but again, the props isn't ready in this state
Save the state on the parent and pass the state with the set Function to the modal. But still it doesn't seems to be right
useEffect but whenever i call setGrams it get a Loop Rendering error, also the props.theValue is still undefined at this point
Example:
In this case, the both TextInput must have a initial value that comes
from the Parent Component and must be manipulated.
You could use Redux for manipulating your data between Components or you can try something like:
set a Global variable:
let previousObj = null
and then set a timeout to update your data only once, even if you set Modal invisible and Visible again
setTimeout(() => {
if(food.grams != undefined && previousObj != food){
previousObj = food
setGrams(""+food.grams)
}
}, 100)
You can either handle it in the child with default value or don't render the component in parent until the value is available:
{theValue && <Component theValue={theValue} />}
You can use useEffect to update the default value as well:
const [grams, setGrams] = useState(0)
useEffect(() => {
if (theValue) {
setGrams(theValue);
}
}, [theValue])
I am working on a vuejs SPA.
I have a view that shows a list of items and another view that shows details for a specific Item.
when I click the item I switch views using:
this.$router.push('/item/' + event.ItemId );
The data is managed using vuex modules.
I would like to allow some temporary display while the item details are being retried (i.e. not to block the rendering of the item details view which should know on its own to indicate that it is still awaiting data).
And I would also have to consider that it should work if the URL is changed (I think I read that there is an issue with the view not being reloaded/recreated when only the item id would change in the URL.
Where would be the appropriate place (code/lifecycle) to trigger the (async) retrieval of the data required for rendering the item details view?
I would like to allow some temporary display while the item details are being retried (i.e. not to block the rendering of the item details view which should know on its own to indicate that it is still awaiting data).
One way to achieve this, is to define a state variable, named e.g. isLoading, in the data context of the Vue component. This variable would then be true while the data is retrieved asynchronously. In the template, you can use v-if to display a spinner while loading, and displaying the content after that.
If you are retrieving the data multiple times (refreshing the view), I would move the retrieving code into a method, e.g. called loadData. In the mounted section of the Vue component you then can just initially call this method once.
Here is some example code:
<template>
<div>
<button #click="loadData" :disabled="isLoading">Refresh</button>
<div class="item" v-if="!isLoading">
{{ item }}
</div>
<div class="spinner" v-else>
Loading...
</div>
</div>
</template>
<script>
import HttpService from '#/services/HttpService';
export default {
name: 'item-details',
data () {
return {
isLoading: false,
item: {}
};
},
methods: {
loadData () {
this.isLoading = true;
HttpService.loadData().then(response => {
this.item = response.data;
this.isLoading = false;
}, () => {
this.item = {};
this.isLoading = false;
});
}
},
mounted () {
this.loadData();
}
};
</script>
And I would also have to consider that it should work if the URL is changed (I think I read that there is an issue with the view not being reloaded/recreated when only the item id would change in the URL.
This issue you mentioned occurs if you are not using the HTML5 history mode, but an anchor (#) in the URL instead. If you are just changing the part after the anchor in the URL, the page is not actually refreshed by the browser. The Vue component won't be reloaded in this case and the state is still old. There are basically two ways around this:
You are switching from anchors in the URL to a real URL with the HTML5 history mode, supported by the Vue Router. This requires some back-end configuration, though. The browser then does not have this faulty behavior, because there is no anchor. It will reload the page on every manual URL change.
You can watch the $route object to get notified on every route change. Depending on if the user is changing the part after the anchor, or before, the behavior is different (it also depends where the cursor is, when you hit enter). If the part after the anchor is changed (your actual Vue route), only the component is notified. Otherwise, a full page refresh is made. Here's some example code:
// ...inside a Vue component
watch: {
'$route' (to, from) {
this.loadData();
}
}
So, in one of my VueJS templates, I have a left sidebar that generates buttons by iterating (v-for) through a multidimensional items array.
When one of these buttons is clicked, a method is run:
this.active.notes = item.notes
active.notes is bound to a textarea in the right content section.
So, every time you click one of the item buttons, you see the (active) notes associated with that item.
I want to be able to have the user edit the active notes in the textarea. I have an AJAX call on textarea blur which updates the db. But the problem is, the items data hasn't changed. So if I click a different item, then click back to the edited item, I see the pre-edited notes. When I refresh the page, of course, everything lines up perfectly.
What is the best way to update the items data, so that it is always consistent with the textarea edits? Should I reload the items data somehow (with another AJAX call to the db)? Or is there a better way to bind the models together?
Here is the JS:
export default {
mounted () {
this.loadItems();
},
data() {
return {
items: [],
active: {
notes: ''
},
}
},
methods: {
loadItems() {
axios.get('/api/items/'+this.id)
.then(resp => {
this.items = resp.data
})
},
saveNotes () {
...api call to save in db...
},
updateActive (item) {
this.active.notes = item.notes;
},
}
}
i can't find items property in your data object.
a property must be present in the data object in order for Vue to convert it and make it reactive
Vue does not allow dynamically adding new root-level reactive properties to an already created instance
maybe you can have a look at this:
Vue Reactivity in Depth
It doesn't seem like this.items exists in your structure, unless there is something that isn't shown. If it doesn't exist set it as an empty array, which will be filled on your ajax call:
data() {
return {
active: {
notes: ''
},
items: [],
},
Now when you ajax method runs, the empty array, items, will be filled with your resp.data via this line:(this.items = resp.data). Then you should be able to iterate through your items array using v-for and your updateActive method should work as you intend it to.
use PUSH
this.items.push(resp.data);
here is a similar question
vue.js http get web api url render list