I'm new to ReactNative, But I have a bit experience in React.
Here I'm trying to store multiple data in AsyncStorage and trying to retrieve them, But I'm only able to store single data
Code:
state = {
data: [],
item: ""
};
storeData = async () => {
await AsyncStorage.setItem("#storage_Key", JSON.stringify(this.state.item));
this.getData();
};
componentDidMount() {
this.getData();
}
getData = async () => {
try {
const value = await AsyncStorage.getItem("#storage_Key");
let { data, item } = this.state;
data.push(value);
this.setState({
data: data,
item: ""
});
} catch (e) {
// error reading value
}
};
Any help or guidance would be appreciated
You are only setting and retrieving one item when you use setItem and getItem respectively.
If you want to store multiple items, you can use multiSet
AsyncStorage.multiSet(['key1', 'value1'], ['key2', 'value2']);
If you want to retrieve multiple items, you can use multiGet
AsyncStorage.multiGet(['key1', 'key2'], (err, items) => {
console.log({ items });
});
I have good experience using
react-native-storage
from what i see from your code you are saving state's item
when you retrieve your data from storage you are putting it in data, is that right?
I suggest you to change the way you arrange the data
you use let { data } = this.state;
if you know you can't set state's data using =
i suggest you to make new variable -> let data and store it with data = value then you can setState({ data: data })
Related
I am working on the backend of my React Native project with Firebase. I want to retrieve records from the database and render a flat ist from an array. I tried to make an array, but outside the function, it is empty again...
Here is my code:
var brand_list = [] // the list
//retrieve record from firebase database
firebase.firestore().collection('Brand').get().then((querySnapshot) => {
querySnapshot.forEach(snapshot => {
let data = snapshot.data();
brand_list.push(data);
})
console.log(brand_list) // contains the records
})
console.log(brand_list) // empty again
What is a possible fix for this, so I can nicely render my flatlist?
In react-native you need to use the state to first initialize the variable and then use setState() to update the variable, setState() functions tell the react-native to update the UI. if the array is empty outside the function that means you need to use async-await here, to make sure you await for a function to be finished first.
I think you try this:
constructor() {
super();
this.state = { dataFetched: [] };
}
async fetchList(){
await firebase.firestore().collection('Brand').get().then((querySnapshot) => {
querySnapshot.forEach(snapshot => {
let data = snapshot.data();
this.setState((prevState)=>{
dataFetched:[...prevState.dataFetched, data]
});
})
console.log(this.state.dataFetched);
// now you can use this.state.dataFetched to update the flatList
}
I'm working on a city based angular application.
getPlaceId function will get the google place_id value.
Based on the place_id getPlacesPhotoRef should return 10 photo ref.
What I'm trying to do is, I wanted the photo ref to be pushed to photo's array.
expected output.
{
formatted_address: 'xxx',
place_id: 'xxx',
photos: [...] //All 10 photo ref
}
But issue is, instead of values, I see Observable getting returned in the photos array.
Below is my code
getPlaceId(cityName) {
let httpPath = `http://localhost:5001/calvincareemailservice/us-central1/webApi/api/v1/getplaces`;
return this.http.post(httpPath, { city: cityName }).subscribe(res => {
if (res) {
let data = JSON.parse(JSON.stringify(res));
this.placeIds.push({
formatted_address: data.candidates[0].formatted_address,
place_id: data.candidates[0].place_id,
photos: this.getPlacesPhotoRef(data.candidates[0].place_id)
.subscribe(res => {
let data = JSON.parse(JSON.stringify(res));
return data.result.photos.map(pic => pic.photo_reference);
})
}
);
}
});
}
getPlacesPhotoRef(id) {
let httpPath = `http://localhost:5001/calvincareemailservice/us-central1/webApi/api/v1/getplacesid`;
return this.http.post(httpPath, { placeId: id })
}
You are very close and thinking about the problem correctly, but the issue is you have assigned an Observable subscription to your photos key rather than the data the .subscribe() actually returned, which I would imagine is what you had hoped you were doing.
At a high level, what you want to do is push a new object to this.placeIds once you have all of the information it needs, e.g. formatted_address, place_id and photos. So what you need to do here is:
Call the /getplaces endpoint and store the place data
Call the /getplacesid endpoint using data.candidates[0].place_id and store the photos data
After both endpoints have returned construct an object using all the data and push this object to this.placeIds
Simple example with nested .subscribe() calls:
getPlaceId(cityName) {
const httpPath = `http://localhost:5001/calvincareemailservice/us-central1/webApi/api/v1/getplaces`;
return this.http.post(httpPath, { city: cityName })
.subscribe(res => {
if (res) {
const data = JSON.parse(JSON.stringify(res));
const formatted_address = data.candidates[0].formatted_address;
const place_id = data.candidates[0].place_id
this.getPlacesPhotoRef(place_id)
.subscribe(res => {
const data = JSON.parse(JSON.stringify(res));
const photos = data.result.photos.map(pic => pic.photo_reference)
this.placeIds.push({
formatted_address,
place_id,
photos
})
})
);
}
});
}
Note: A more elegant way to do this would be to use concatMap
I have a three-check box type,
When I check any box I call refetch() in useEffect().
The first time, I check all boxes and that returns the expected data!
but for some cases "rechange the checkboxes randomly", the returned data from API is "undefined" although it returns the expected data in Postman!
So I Guess should I need to provide a unique queryKey for every data that I want to fetch
so I provide a random value "Date.now()" but still return undefined
Code snippet
type bodyQuery = {
product_id: number;
values: {};
};
const [fetch, setFetch] = useState<number>();
const [bodyQuery, setBodyQuery] = useState<bodyQuery>({
product_id: item.id,
values: {},
});
const {
data: updatedPrice,
status,
isFetching: loadingPrice,
refetch,
} = useQuery(
['getUpdatedPrice', fetch, bodyQuery],
() => getOptionsPrice(bodyQuery),
{
enabled: false,
},
);
console.log('#bodyQuery: ', bodyQuery);
console.log('#status: ', status);
console.log('#updatedPrice: ', updatedPrice);
useEffect(() => {
if (Object.keys(bodyQuery.values).length > 0) {
refetch();
}
}, [bodyQuery, refetch]);
export const getOptionsPrice = async (body: object) => {
try {
let response = await API.post('/filter/product/price', body);
return response.data?.detail?.price;
} catch (error) {
throw new Error(error);
}
};
So after some elaboration in the chat, this problem can be solved by leveraging the useQuery key array.
Since it behaves like the dependency array in the useEffect for example, everything that defines the resulted data should be inserted into it. Instead of triggering refetch to update the data.
Here the key could look like this: ['getUpdatedPrice', item.id, ...Object.keys(bodyQuery.values)], which will trigger a new fetch if those values change and on initial render.
I need to display some data in my component, unfortunately the first call to my API returns just part of the information I want to display, plus some IDs. I need another call on those IDs to retrieve other meaningful data. The first call is wrapped in a useEffect() React.js function:
useEffect(() => {
const getData = async () => {
try {
const { data } = await fetchContext.authAxios.get(
'/myapi/' + auth.authState.id
);
setData(data);
} catch (err) {
console.log(err);
}
};
getData();
}, [fetchContext]);
And returns an array of objects, each object representing an appointment for a given Employee, as follows:
[
{
"appointmentID": 1,
"employeeID": 1,
"customerID": 1,
"appointmentTime": "11:30",
"confirmed": true
},
... many more appointments
]
Now I would like to retrieve information about the customer as well, like name, telephone number etc. I tried setting up another method like getData() that would return the piece of information I needed as I looped through the various appointment to display them as rows of a table, but I learned the hard way that functions called in the render methods should not have any side-effects. What is the best approach to make another API call, replacing each "customerID" with an object that stores the ID of the customer + other data?
[Below the approach I've tried, returns an [Object Promise]]
const AppointmentElements = () => {
//Loop through each Appointment to create a single row
var output = Object.values(data).map((i) =>
<Appointment
key={i['appointmentID'].toString()}
employee={i["employeeID"]} //returned a [Object premise]
customer={getEmployeeData((i['doctorID']))} //return a [Object Promise]
time={index['appointmentTime']}
confirmed = {i['confirmed']}
/>
);
return output;
};
As you yourself mentioned functions called in the render methods should not have any side-effects, you shouldn't be calling the getEmployeeData function inside render.
What you can do is, inside the same useEffect and same getData where you are calling the first api, call the second api as well, nested within the first api call and put the complete data in a state variable. Then inside the render method, loop through this complete data instead of the data just from the first api.
Let me know if you need help in calling the second api in getData, I would help you with the code.
Update (added the code)
Your useEffect should look something like:
useEffect(() => {
const getData = async () => {
try {
const { data } = await fetchContext.authAxios.get('/myapi/' + auth.authState.id);
const updatedData = data.map(value => {
const { data } = await fetchContext.authAxios.get('/mySecondApi/?customerId=' + value.customerID);
// please make necessary changes to the api call
return {
...value, // de-structuring
customerID: data
// as you asked customer data should replace the customerID field
}
}
);
setData(updatedData); // this data would contain the other details of customer in it's customerID field, along with all other fields returned by your first api call
} catch (err) {
console.log(err);
}
};
getData();
}, [fetchContext]);
This is assuming that you have an api which accepts only one customer ID at a time.
If you have a better api which accepts a list of customer IDs, then the above code can be modified to:
useEffect(() => {
const getData = async () => {
try {
const { data } = await fetchContext.authAxios.get('/myapi/' + auth.authState.id);
const customerIdList = data.map(value => value.customerID);
// this fetches list of all customer details in one go
const customersDetails = (await fetchContext.authAxios.post('/mySecondApi/', {customerIdList})).data;
// please make necessary changes to the api call
const updatedData = data.map(value => {
// filtering the particular customer's detail and updating the data from first api call
const customerDetails = customersDetails.filter(c => c.customerID === value.customerID)[0];
return {
...value, // de-structuring
customerID: customerDetails
// as you asked customer data should replace the customerID field
}
}
);
setData(updatedData); // this data would contain the other details of customer in it's customerID field, along with all other fields returned by your first api call
} catch (err) {
console.log(err);
}
};
getData();
}, [fetchContext]);
This will reduce the number of network calls and generally preferred way, if your api supports this.
How can i send a request to graphql using relay onclick ?
render(){
<div>
<img src={this.state.picture}>
<input type="email" value={this.state.email} onChange{...}/>
<button onClick={this.checkEmail}>Check</button>
</div>
}
checkEmail = async () => {
const res = await axios({
method: 'post',
url: __RELAY_API_ENDPOINT__,
data: {
query: `query CheckEmail($email: String!){lookupEmail(email: $email){id, picture}}`,
variables: {"email": this.state.email}
}
});
//set state using res
}
I cant figure out how to do this with relay.
In the examples relay is used to fetch and render onMount.
But how would i get data and change state on event listeners (onclick) ?
I couldnt find any example like that .
you can declare data dependency in relay but in some cases when you had a paginationcontainer which will fetch not 'all' e.g. first: 10 so we cannot get the length of it, in this case, you need to declare another data dependency by doing request. I hope you understand what I'm trying to say.
This is how i do it in my code, u need to explore the relay props more:
getPublicTodosLengthForPagination = async () => { // get publicTodos length since we cannot get it declared on createPaginationContainer
const getPublicTodosLengthQueryText = `
query TodoListHomeQuery {# filename+Query
viewer {
publicTodos {
edges {
cursor
node {
id
}
}
pageInfo { # for pagination
hasPreviousPage
startCursor
hasNextPage
endCursor
}
}
}
}`
const getPublicTodosLengthQuery = { text: getPublicTodosLengthQueryText }
const result = await this.props.relay.environment._network.fetch(getPublicTodosLengthQuery, {}) // 2nd arguments is for variables in ur fragment, in this case: e.g. getPublicTodosLengthQueryText but we dont need it
return await result.data.viewer.publicTodos.edges.length;
}
componentDidMount = async () => {
let result = await this.getPublicTodosLengthForPagination();
this.setState((prevState, props) => {
return {
getPublicTodosLength: result
}
});
}
implement this on ur code then update me.best of luck!