store locally either in asyncStorage or in a state - react-native

I use redux for state management. Now I have a json object of 1000 objects that I need to store locally either in asyncStorage or in a state to use it in different part of my application. What is the best way to handle this json data
const initialState = {
loading: false,
dataAppDB:{}
}
try {
await fetch(url)
.then(response => response.json())
.then(data => {
thunkAPI.dispatch(saveDataApp(data));
let data1 = JSON.stringify(data);
AsyncStorage.setItem('dataAppDB', data1);
});
} catch (e) {
}
}
}
Is it normal to store these 1000 json objects in for example in state : dataAppDB = {}
and to be able to use it in my application

That will depend on one simple question. Do you need the data to persist after the app was closed?
If yes, then using AsyncStorage is a valid approach. Of course, you can also have a proper backend with a database, but AsyncStorage will do the job.
If not, then what you want is to use a context, for this you have different options. A common one is the hook useContext, but redux is also a very good alternative.

Related

Issue rendering data from firestore document in react native

I created a map for the array of exercises in my database, and then for each exercise, which is a document reference, I'm getting the data from that document reference and setting it to a state. This is resulting in an infinite loop right now.
If I remove the setExerciseData line, the console logs the exercise object's data that I'm expecting to see. I'm really not sure what the correct way to render the name field from this data is.
{workout.exercises.map((exercise) => {
async function getData(exercise) {
getDoc(exercise).then((doc) => {
console.log(doc.data());
setExerciseData(doc.data());
});
}
getData(exercise);
return (
<Text>{exerciseData.name}</Text>
)
})}
You need to use useEffect() and setState() to be able to render your data. Also, Firebase Firestore is Asynchronous in nature, as a general note, no one should be trying to convert an Async operation into a sync operation as this will cause problems. You need to use an Asynchronous function to fetch data from Firestore. See sample code below:
const getExerciseData = async () => {
const docRef = doc(db, "<collection-name>", '<document-id>')
const docSnap = await getDoc(docRef)
if (docSnap.exists()) {
// console.log("Document data:", docSnap.data())
setExerciseData(docSnap.data())
} else {
// doc.data() will be undefined in this case
console.log("No such document!")
}
}
useEffect(() => {
getExerciseData()
}, [])
return (
<Text>{exerciseData.name}</Text>
)
You could also check my answer on this thread for more use-cases.

Vue - Best way to poll the same API endpoint for multiple components simultaneously

I need to fetch continuously updating API endpoint data for several components on my NUXT site, 2 of which are visible simultaneously at any given moment. I wonder what is the best practice to poll the same endpoint for several different components visible at the same time?
Currently, I am using VUEX to send the API data to my components and then using setInterval with a refresh function to update the data in each component. This is clearly a clumsy solution (I am a beginner). I was thinking about polling the API directly in VUEX but I understand this is not advisable either.
This is my current (clumsy) solution:
VUEX:
// STATE - Initial values
export const state = () => ({
content: {}
});
// ACTIONS
export const actions = {
async nuxtServerInit ({ commit }) {
const response = await this.$axios.$get('https://public.radio.net/stations/see15310/status');
commit('setContent', response)
}
}
// MUTATIONS
export const mutations = {
setContent(state, content) {
state.content = content;
}
}
And in each component:
computed: {
content () {
return this.$store.state.content
},
methods: {
refresh() {
this.$nuxt.refresh()
}
},
mounted() {
window.setInterval(() => {
this.refresh();
}, 5000);
I think, it's a normal solution to do the polling in vuex. Vuex is your application state and the one source of truth for all dependant components. And if you need to update some similar state for different components - it's a rational solution to do it in vuex action.
Another solution could be the event bus. First article about it from google
Also, I don't recommend use SetInterval for polling. Because it's don't wait of async operation ending. This fact could shoot you in the foot, if a client has network delay or another glitch. I used SetTimeout for this purpose.
async function getActualData() {
// get data from REST API
}
async function doPolling() {
const newData = await getActualData() // waiting of finish of async function
// update vuex state or send update event to event bus
setTimeout(doPolling, 5000)
}
doPolling()
If my answer missed into your question, then give me more details please. What is the problem you want to solve? And what disadvantages do you see in your(by your words ;) ) "clumsy" solution?

How to properly initialize Vuex state to avoid additional API calls

Within a records module, there is an action, getRecords to retrieve all records from the API at /records, which commits the setRecords mutation which sets the state for records. A getter for records also exists.
Within the records vue, the created method calls the getRecords action, and the getter for records is passed to the datatable for display.
Until now everything works, however when navigating on and off the records vue, the API is called each time.
How is the properly handled? Does a best practice exist? I can move the action call to a higher level, but this generates an API that may not be required id the user never visits the records vue.
records module
const getters = {
allRecords: state => state.records
}
const state = {
records: []
}
const actions = {
async getRecords({commit}){
console.log('getting records...');
const response = await axios.get('/api/records')
commit('setRecords', response.data)
},
async addRecord({ commit }, user) {
console.log("user :" + JSON.stringify(user))
const response = await axios.post('/api/records', user)
.catch(err => console.log(err))
commit('addRecord', response.data)
}
}
const mutations = {
setRecords: (state, records) => (state.records = records),
addRecord: (state, user) => ([...state.records, user])
}
export default {
state,
getters,
actions,
mutations
}
I have handled this in various different ways in the past.
If you do not care if the data you are serving might be old, you can simply detect if you already have some items in your array:
const actions = {
async getRecords({ commit, getters }){
if (getters.allRecords.length > 0) {
// Don't bother retrieving them anymore
return;
}
console.log('getting records...');
const response = await axios.get('/api/records')
commit('setRecords', response.data)
},
async addRecord({ commit }, user) {
console.log("user :" + JSON.stringify(user))
const response = await axios.post('/api/records', user)
.catch(err => console.log(err))
commit('addRecord', response.data)
}
}
If you do want to have the updated values in your database, you can consider changing your api to only return changed records after a certain timestamp. For this we need the following:
We need to store the timestamp of our last update. The first time we would retrieve everything, and on subsequent requests we would send the timestamp.
A way to identify which records to update in the local state, and which records to delete or add. Having something like an id on your records might be helpful.
Let's assume that instead of returning a flat array of your records, the api returns a response in the format
{
records: [ ... ],
removedRecords: [ ... ],
timestamp: 123456789
}
You would change your state to
const state = {
records: [],
recordUpdateTimestamp: null
}
Your action would then look something like this:
async getRecords({ commit, state }){
const config = {};
if (state.recordUpdateTimestamp) {
config.params = {
timestamp: state.recordUpdateTimestamp
};
}
console.log('getting records...');
const { data }= await axios.get('/api/records', config)
commit('setRecords', data.records);
commit('removeRecords', data.removedRecords);
commit('setRecordUpdateTimestamp', data.timestamp);
},
I will leave writing the mutations to you.
This would obviously need work in the backend to determine which records to send back, but may have the advantage of cutting down both the amount of returned data and the time processing that data a lot.
FYI you don't need a shallow getter like the one you have.
Meaning that a getter that doesn't compute your state has no value might as well use the state itself.
About the practice, it really depends on how important it is to you that "records" has always the freshest data. If you don't need it to be always fetched, you can have a "initRecords" action to run on your "App.vue" on created hook that you can use to initialize your records. If you need always fresh data, what you have is good enough.

In Redux, how to get state in action? [duplicate]

This question already has answers here:
Accessing Redux state in an action creator?
(8 answers)
Closed 3 years ago.
In below example, after I get data from firebase, I want to add user object which is already present in redux store and append it in all the message objects. Request you to help.
Questions:
Is it a good approach to access state in action creator? If yes, how?
If not, what are the alternatives?
db.collection(`messages/${documentId}/chat`)
.get()
.then(snapshot => {
const messages = [];
snapshot.docs.forEach(doc => {
console.log("message", doc.id, doc.data());
messages.push({
id: doc.id,
from: doc.data().from,
to: doc.data().to,
text: doc.data().text,
timestamp: doc.data().timestamp.seconds * 1000
});
});
dispatch({ type: GET_CHAT, payload: messages });
});
if your using Redux Thunk middleware, you could simply do that:
const myAction = () => async (dispatch, getState) => {
const { field1, field2 } = getState().myReducer;
};
Simply.
import store from "..path/to/your/createStore/export".
then use it inside action or anywhere, where we cannot use connect method of redux to subscribe to our component.
store.getState() // will return the whole store object.
other useful method to dispatch action;.
store.dispatch(action name).
Lastly, soter.getState() should be used in case of emergency.
for your use case .
mostly we work with action arguments. And pass data as parameters of actions. From where we dispatch actions ex. from some component which is subscribed to redux store. there we will access to store object via the mapStateToProps method.
export const successBuy = data => {
let request = axios
.post(`/api/users/successBuy`, data)
.then(response => response.data);
return {
type: actionTypes.SET_CURRENT_USER,
payload: request
};
};

Redux + Rest API + Realm JS in React Native

I am developing an app that controls IOT devices via REST API.
I am using Realm database inside the app to keep historical data captured by IOT sensors. I also decided to use it to persist information about user and devices. redux-persist didn't seem like the best choice since the app will eventually deal with big tables of data of historical data.
I am now building my Redux actions with Redux-Thunk and I am in doubt about what would be the ideal workflow/data flow. I am currently calling realm inside redux actions like this:
function addNew(deviceIp) {
const request = () => { return { type: c.ADD_REQUEST } };
const success = (payload) => { return { type: c.ADD_SUCCESS, payload} };
const failure = (payload) => { return { type: c.ADD_FAILURE, payload } };
return async (dispatch,getState) => {
dispatch(request());
try {
const res = await apiService.getIt(deviceIp + urls.general);
// Convert responses to date before Realm Insertion
const newDevice = {
...res.deviceInfo[0],
host: deviceIp,
manufacturingDate: new Date(res.deviceInfo[0].manufacturingDate),
lastFwUpdateDate: new Date(res.deviceInfo[0].lastFwUpdateDate),
firstLaunchDate: new Date(res.deviceInfo[0].firstLaunchDate),
lastResetDate: new Date(res.deviceInfo[0].lastResetDate)
};
const addedDevice = await realmActions.createNew('Device',newDevice);
dispatch(success(addedDevice));
}
catch (error)
{
dispatch(failure(error));
}
};
}
realmActions.createNew(collectionName, newEntry) is a method I have created to add new entries to the specified collection in Realm DB. I have one main concern about this methodology.:
It seems to me a bit of an overkill to write objects to both Realm and Redux. But Realm will be useful to persist this data in case the user closes and re-opens the app. What do you think about the approach I am taking. Would you suggest anything cleaner or smarter?