updating the state in componentWillMount - react-native

i want to create a object with multiple object. the data is something like this
dataList = [{inputFieldId: 1, dataField:{...}, data: '120'}, {inputFieldId: 2, dataField:{...}, data: '120'} ]
what is want like this.
res = [{1: '120'}, {2: '120'}]
i write a code for this but its giving me the last object data only.
constructor(){
super()
this.state = {
inputValue:{},
datalist = [],
}
}
componentDidMount() {
console.log(this.state.inputValue)
this.props.navigation.setParams({ sendDataToServer:
this._sendDataToServer });
}
async componentWillMount(){
for(var key in dataList){
this.setState({
inputValue: {
...this.state.inputValue,
[dataList[key].inputFieldId]: dataList[key].data
}
})
}
}
code output = { 2: '120'}
thanks in advance

setState work asynchronously. Instead of this
this.setState({
inputValue: {
...this.state.inputValue,
[dataList[key].inputFieldId]: dataList[key].data
}
})
Try to change to this
this.setState((previousState) => ({
inputValue: {
...previousState.inputValue,
[dataList[key].inputFieldId]: dataList[key].data
}
}))

Related

How to pass a computed as a prop to a component?

I have 1 component to which I pass a computed as a prop in this way:
<Datatable :extraParams="extraParams" />
the computed is in the attached image.
I'm having trouble with the value of this property: coverageSelected:coverageData
Coverage data is filled by a select multiple
The problem I have is that when selecting an element of the select, first the component function is executed, then the coverageSelected property is empty, then the computed is executed and until this moment the coverageSelected array is filled, then until the second attempt It already has a full array.
This is my computed
props: [
"status_selected",
"rows",
"totals",
"dateRangeValue",
"coverageSelected",
"coverageList",
"showAll",
"dateFilterSelected",
],
computed(){
extraParams() {
let coverageData = this.coverageList.filter((m) => this.coverageSelected.includes(m.variable));
return {
status: this.status_selected,
dateRange: this.dateRangeValue,
dateFilterSelected: this.dateFilterSelected,
coverageSelected: coverageData, //This is the property that is not late.
showAll: this.showAll,
};
},
}
Another detail to mention that this.coverageSelected is a prop
The method that is executed first in the computed is this:
async getList(params) {
this.loading = true;
try {
if (params) {
this.query = { ...this.query, ...params, ...this.extraParams, filters: this.filters };
} else {
this.query = { ...this.query, ...this.extraParams, filters: this.filters };
}
const { data } = await this.$axios.post(`${this.$config.routePrefix}${this.action}`, this.query);
if (data.code == 200) {
this.rows = data.rows;
this.total = data.total;
this.$emit("listed", data);
}
} finally {
this.loading = false;
}
},

To-dos don't update when I choose a to-do list

I have two components: TodoList and TodoListsList. They get their data from states in todos.js and todoLists.js modules accordingly. When I choose some to-do list, i.e mark it as active, TodoListsList is updated, but TodoLists isn't, thought the data is updated. Here's how I do it.
todoListsState and markAsActive() (todoLists.js):
import todos from '#/modules/todos.js'
// ... some code ...
const todoListsState = reactive({
todoLists: [],
todoListsAreLoading: false,
removedTodoListId: null,
editedTodoListId: null,
editedTodoListName: '',
baseTodoListsApiUrl: process.env.VUE_APP_BASE_TODO_LISTS_API_URL,
todoListCreationFormModalId: 'todoListCreationFormModal',
todoListNameChangeFormModalId: 'todoListNameChangeFormModal'
});
// ... some code ...
function markAsActive(value) {
let { close } = infoToast();
if (value) {
axios.post((todoListsState.baseTodoListsApiUrl + 'mark-as-active'), {
activatedTodoListId: value
}).then(function () {
getTodoLists();
const { getTodos } = todos();
getTodos();
}).catch(function () {
dangerToast('Failed to mark to-do list as active.');
}).finally(() => {
close();
});
}
}
todosState and getTodos() (todos.js):
const todosState = reactive({
todos: [],
activeTodoListId: 0,
removedTodoId: null,
editedTodoId: null,
editedTodoText: '',
todosAreLoading: false,
baseTodosApiUrl: process.env.VUE_APP_BASE_TODOS_API_URL,
todoAdditionFormModalId: 'todoAdditionFormModal',
todoEditFormModalId: 'todoEditFormModal'
});
// ... some code ...
async function getTodos() {
try {
todosState.todosAreLoading = true;
const response = await axios.get(todosState.baseTodosApiUrl);
todosState.activeTodoListId = response.data[0];
todosState.todos = response.data[1];
} catch (e) {
dangerToast('To-dos loading failed.');
} finally {
todosState.todosAreLoading = false;
}
}
How does todosState.todos look in console:
todosState.todos when Todos.vue is mounted:
It doesn't look like the array looses it's reactivity.
If you need something else to understand my question, feel free to ask. Help appreciated.
The problem is solved! I have just moved todosState out of
export default function () {}
and it works! Finally! This thread helped me a lot.

getters not reactive in vuex

I have following store defined:
state: () => ({
infoPackCreationData: null,
infoPackCreationTab: null,
}),
getters: {
infoPackImage(state: any) {
return state.infoPackCreationTab && state.infoPackCreationTab.infopackContents
? state.infoPackCreationTab.infopackContents.filter((item: any) => item.type === "IMAGE")
: [];
}
},
mutations: {
setImageData(state:any, infopackImageData: any) {
state.infoPackCreationTab.infopackContents.filter((item: any) => {if(item.type === "IMAGE")
item = infopackImageData
console.log(item , 'this is items');
return item})
}
},
actions: {
setImageData(context: any, payload: any) {
context.commit('setImageData', payload)
}
}
and in my component I am using the computed to get the imageList:
computed: {
...mapGetters("creationStore", ["infoPackImage"]),
imageList: {
get() {
return this.infoPackImage ?? [];
},
set(value) {
this.$store.dispatch('creationStore/setImageData', value);
}
}
},
The problem is I want to edit a value of the imageList by index using draggable libarary,
but imageList does not act reactive and it just move the image and not showing the other image in the previous index:
async imageChange(e) {
this.loading = true
let newIndex = e.moved.newIndex;
let prevOrder = this.imageList[newIndex - 1]?.order ?? 0
let nextOrder = this.imageList[newIndex + 1]?.order ?? 0
const changeImageOrder = new InfopackImageService();
try {
return await changeImageOrder.putImageApi(this.$route.params.infopackId,
this.$route.params.tabId,
e.moved.element.id, {
title: e.moved.element.title,
infopackAssetRef: e.moved.element.infopackAssetRef,
order: nextOrder,
previousOrder: prevOrder,
}).then((res) => {
let image = {}
let infopackAsset = e.moved.element.infopackAsset
image = {...res, infopackAsset};
Vue.set(this.imageList, newIndex , image)
this.loading = false
return this.imageList
});
} catch (e) {
console.log(e, 'this is put error for tab change')
}
},
Array.prototype.filter doesn't modify an array in-place, it returns a new array. So this mutation isn't ever changing any state:
mutations: {
setImageData(state:any, infopackImageData: any) {
state.infoPackCreationTab.infopackContents.filter((item: any) => {if(item.type === "IMAGE")
item = infopackImageData
console.log(item , 'this is items');
return item})
}
},
So, if you intend to change state.infoPackCreationTab.infopackContents, you'll need to assign the result of filter():
mutations: {
setImageData(state:any, infopackImageData: any) {
state.infoPackCreationTab.infopackContents = state.infoPackCreationTab.infopackContents.filter(...)
However, since state.infoPackCreationTab did not have an infopackContents property during initialization, it will not be reactive unless you use Vue.set() or just replace the whole infoPackCreationTab object with a new one (see: Vuex on reactive mutations):
mutations: {
setImageData(state:any, infopackImageData: any) {
state.infoPackCreationTab = {
...state.infoPackCreationTab,
infopackContents: state.infoPackCreationTab.infopackContents.filter(...)
};

React-Native AsyncStorage: I retrieve an array, but then it becomes only a single object of the array

I'm using AsyncStorage to store and retrieve an array of objects. The structure of the array is like this:
const tracks = [
{
title: title1,
exercises: [
{
object1Info...
},
{
object2Info...
}
]
},
{
title: title2,
exercises: [
{
object1Info...
}
]
},
{
title: title3,
exercises: [
{
object1Info...
}
]
}
]
As you can see, the objects in the array do themselves contain arrays, which again contain objects.
I'm storing the array like this:
const storeData = async (array) => {
try {
const stringifiedArray = JSON.stringify(array)
await AsyncStorage.setItem('#tracks_array', stringifiedArray)
} catch (e) {
console.log("Error saving data")
}
}
This seems to work fine. I then retrieve the data like this:
const retrieveData = async () => {
try {
const jsonValue = await AsyncStorage.getItem('#tracks_array');
console.log('Parsed value: ' + JSON.parse(jsonValue)); //This prints 'Parsed value: [object Object],[object Object],[object Object],[object Object]'
return jsonValue !== null ? JSON.parse(jsonValue) : null;
} catch (e) {
console.log("Error retrieving data")
}
}
This seems to work fine as well.
I have the array stored also as state. So what I want to do is add an object to the array in state, store that new array in the AsyncStorage, and then retrieve the array and set this new array back to state. Storing the object seems to have no problems.
When I retrieve the new array, and console.log(JSON.parse(jsonValue)) inside retrieveData, it prints [object Object],[object Object],[object Object],[object Object]. However after I call const newData = retrieveData(), console.log(newData) prints just [object Object]. This is my first time using AsyncStorage so I must be misunderstanding something. Why does it only return one object, instead of the whole array?
EDIT: Sharing the whole component code:
import {
StyleSheet,
ScrollView,
View,
Text
} from 'react-native';
import Modal from 'react-native-modal';
import AsyncStorage from '#react-native-community/async-storage'
import Track from './Track.js';
import New from './New.js';
class Main extends Component {
constructor(props) {
super(props);
this.state = {
tracksData: tracks,
newTrack: false,
newExercise: false
}
storeData(this.state.tracksData);
}
renderTracks(data) {
console.log('Main data = ' + data)
return data.map((item, i) => {
console.log('Item = ' + item)
return (
<Track key={i} data={item} />
)
});
}
render() {
return (
<ScrollView horizontal={true} style={styles.Main}>
{this.renderTracks(this.state.tracksData)}
<Track data={{title: 'NewTrack', exercises: 'NewTrack'}} newTrackBox={this.toggleTrackBox} />
<Modal isVisible={this.state.newTrack} coverScreen={true}>
<New type={'track'} visible={this.toggleTrackBox} add={(name) => this.addTrack(name)}/>
</Modal>
</ScrollView>
);
}
toggleTrackBox = () => {
this.setState({
newTrack: !this.state.newTrack
})
}
addTrack = (name) => {
this.setState({
newTrack: false
});
var newTracks = this.state.tracksData;
newTracks.push({title: name, exercises: []})
console.log('newTracks = ' + newTracks)
storeData(newTracks);
this.updateData();
}
updateData() {
var newData = retrieveData();
console.log('newData = ' + newData)
setTimeout(() => {
console.log('Retrieved data = ' + newData);
if (newData) {
this.setState({
tracksData: newData
});
console.log("Data updated");
return true;
} else {
console.log("Data couldn't be retrieved");
return false;
}
}, 5000)
}
}
const storeData = async (value) => {
try {
const stringifiedArray = JSON.stringify(value)
console.log('Value to store: ' + value)
console.log('Stringified value to store: ' + stringifiedArray)
await AsyncStorage.setItem('#tracks_array', stringifiedArray)
//alert("Success saving data!")
} catch (e) {
console.log("Error saving data")
alert("Error saving data");
}
}
const retrieveData = async () => {
try {
const jsonValue = await AsyncStorage.getItem('#tracks_array');
console.log('Stringified value retrieved: ' + jsonValue)
console.log('Parsed value: ' + JSON.parse(jsonValue))
return jsonValue !== null ? JSON.parse(jsonValue) : null;
} catch (e) {
console.log("Error retrieving data")
alert("Error retrieving data");
}
}
const tracks = [ //each member of this array is sent to a Track
{
title: 'Pull-up', // used in Track
exercises: [ // each member of this array is sent to an Exercise by Track
{
name: 'Pull-up', // used in Exercise
setStart: 2, // this and below used to calculate no of tiles and their contents, which are then sent to Tile
setEnd: 3,
repStart: 5,
repEnd: 8,
isInSeconds: false,
inProgress: null,
completed: true
},
{
name: 'Weighted Pull-up',
setStart: 3,
setEnd: 3,
repStart: 5,
repEnd: 8,
isInSeconds: false,
inProgress: [3, 5],
completed: false
}
]
},
{
title: 'Dip',
exercises: [
{
name: 'Dip',
setStart: 2,
setEnd: 3,
repStart: 5,
repEnd: 8,
isInSeconds: false,
inProgress: null,
completed: true
}
]
},
{
title: 'Squat',
exercises: [
{
name: 'Pistol squat',
setStart: 2,
setEnd: 3,
repStart: 5,
repEnd: 8,
isInSeconds: false,
inProgress: [2, 8],
completed: false
}
]
}
]
const styles = StyleSheet.create({
Main: {
flex: 1,
flexDirection: 'row',
backgroundColor: '#022763'
}
})
export default Main;
Also, I should have mentioned, the actual error I'm getting is:
TypeError: undefined is not a function (near '...data.map...')
"retrieveData" is async function and hence returns a Promise.
What happened was it didn't finish retrieving the data and hence newData got 1 object out of all the array.
Try changing updateData like this:
updateData() {
var newData = retrieveData().then(data => {
console.log('newData = ' + newData)
setTimeout(() => {
console.log('Retrieved data = ' + newData);
if (newData) {
this.setState({
tracksData: newData
});
console.log("Data updated");
return true;
} else {
console.log("Data couldn't be retrieved");
return false;
}
}, 5000)
};
}
I've figured out the issue. I was retrieving data with AsyncStorage, then setting that data to the state something like this:
var newData = asyncRetrieveDataFunction();
this.setState({state1: newData})
However, because I declared the retrieveData() function as async, it was setting the state before the data had finished retrieving. The solution was to use the then keyword and change it to something like this:
asyncRetrieveDataFunction().then(data => this.setState({state1: data}));
This ensures that the data has been returned BEFORE assigning it to a state.

Fetching multiple API requests with React Native

Here is an outline of my code (sparing some details). Basically I just want to make two similar API requests when I click a button, then have a function that works with the results of both the requests, but I cannot figure it out.
class myClass extends Component {
constructor(props) {
super(props);
this.API_KEY_ONE = ‘firstapikey’
this.API_KEY_TWO = ‘secondapikey’
this.state = {
city: 'undefined',
state: 'undefined'
}
}
callOne() {
this.REQUEST_URL = 'https://api.wunderground.com/api/' + this.API_KEY_ONE + '/geolookup/conditions/astronomy/forecast/q/.json';
fetch(this.REQUEST_URL).then((response) => response.json()).then((responseData) => {
this.setState({
city: responseData.location.city
});
}).done();
}
callTwo() {
this.REQUEST_URL = 'https://api.DifferentSite.com/api/' + this.API_KEY_TWO + '/geolookup/conditions/astronomy/forecast/q/.json';
fetch(this.REQUEST_URL).then((response) => response.json()).then((responseData) => {
this.setState({
state: responseData.location.state
});
}).done();
}
// where to put this? when both requests finish, pass both to new component
this.props.navigator.push({
title: 'Forecast',
component: Forecast,
passProps: {city: this.state.city, state: this.state.state}
});
getForecast() {
this.callOne();
this.callTwo();
}
<TouchableHighlight onPress={() => this.getForecast()} />
You can continue with .then() so it should be something like this:
callBoth(){
var request_1_url = 'https://api.wunderground.com/api/' + this.API_KEY_ONE + '/geolookup/conditions/astronomy/forecast/q/.json';
var request_2_url = 'https://api.DifferentSite.com/api/' + this.API_KEY_TWO + '/geolookup/conditions/astronomy/forecast/q/.json';
fetch(request_1_url).then((response) => response.json()).then((responseData) => {
this.setState({
city: responseData.location.city
});
}).then(()=>{
fetch(request_2_url).then((response) => response.json()).then((responseData) => {
this.setState({
state: responseData.location.state,
isFinish: true
});
}).done();
}).done();
}
1) It seems you are using city and state as passProps and not going to refresh the currentView, so maybe you should use them as variables of the current component.
2) You can simply use a variable to record the state of fetching. Like set _finish = 0, when city is fetched, _finish = _finish + 1, and validate whether _finish equals 2. When state is fetched, do the same validate.
fetch(...){
// if _finish is equals 2, navigator push.
}
3) Or you can do it without extra variable:
fetch(...){
if (this._city && this._state){
// navigator push
}
}