How do MongoDB Stitch SDK's work in regards to client instantiation or how does Stitch.defaultAppClient.getServiceClient work? - react-native

I'm using expo to build out a React Native application and I'm running into issues when attempting to write code that accesses remote MongoDB servers. I'm attempting to use MongoDB's provided Stitch SDK's for React Native.
When running
const mongoClient = Stitch.defaultAppClient.getServiceClient(RemoteMongoClient.factory, "mongodb-atlas");
I'm running into the following error:StitchServiceError: service not found: 'mongodb-atlas'
When my app initializes in my main App component, I'm initializing the default client using Stitch.initializeDefaultAppClient per the recommended documentation. Based on my debugging logs, this part is working correctly and I'm able to authenticate with the service correctly and I am storing the client in the App component's state. I'm running the loadClient method in the constructor of my main App component.
_loadClient() {
console.log("Loading Stitch client");
Stitch.initializeDefaultAppClient("xxxxxxxxxxxxxxx").then(client => {
this.setState({ client });
this.state.client.auth
.loginWithCredential(new AnonymousCredential())
.then(user => {
console.log(`Successfully logged in as user ${user.id}`);
this.setState({ currentUserId: user.id });
this.setState({ currentUserId: client.auth.user.id });
})
.catch(err => {
console.log(`Failed to log in anonymously: ${err}`);
this.setState({ currentUserId: undefined });
});
});
}
For more context: I'm executing the getServiceClient function in a separate react saga so that I can fetch data behind the scenes based on actions that are dispatched within the application. I'm calling getServiceClient inside a function that gets called upon every dispatch of a specific action. All of this is exported to a single async function which is then applied as saga middleware enhancer to a store.
I think I'm not able to retrieve the service client because the defaultappclient isn't initialized within the context of the saga because of the way sagas work (from my understanding) but I need more insight into how getServiceClient() works.

I ended up storing the client in a local instance in the saga js file so that the instance is available for all sagas and I plan on keeping all sagas within this file. I am using asynchronous functions to ensure that the app client is initialized prior to binding any client requests to redux actions.
Example:
let appClient;
function* initAppClient() {
console.log("Initializing Stitch Client");
yield Stitch.initializeDefaultAppClient("client-identification-here ").then(client => appClient=client);
}
export default function* rootSaga() {
yield initAppClient();
yield takeEvery('ACTION HERE', uploadState);
}
The downside to this approach is that this instance won't be available to the rest of my react application and I won't be able to use Stitch functionality to update anything through the actual react application. This works for me as I only plan on using Stitch when state changes within my application and this decouples any server/remote data operations from react application functionality which focuses on presentation, routing, etc. If I want to use Stitch within my react application, I would have to initialize another client within react's context.

Related

Update all clients after a change in vuex state

I am using vue2 syntax and vuex , versions : vue/cli 4.5.13 and vue#2.6.14 and vuex 3.6.2
I have a simple to do project , for adding todos in a list, based on the 2019 vue tutorial by traversy.
I have a simple form ij my component to add a to do
<form #submit.prevent="onSubmit" >
and in my vuex store I have
const state = {
todos:''
};
const getters = {
allTodos: (state) => {return state.todos}
};
const actions = {
async addTodo({commit}, title){
const res = await axios.post('https://jsonplaceholder.typicode.com/todos', {
title,
completed:false
});
commit('newTodo', res.data);
}
};
const mutations = {
newTodo:(state, todo)=>(
state.todos.unshift(todo)
)
};
Is there a way to update all clients that view the todos, without clients have to refresh nothing, as soon as a new todo is added in the state, using only vuex/vue?
Thank you
Is there a way to update all clients that view the todos, without clients have to refresh nothing, as soon as a new todo is added in the state, using only vuex/vue?
No, it is not possible.
There is no link between all your clients. All your Vue/VueX code lives in a single client. Here's what you need to do to get where you want to go, and its a long way from here:
Build a backend server. Here's a Node.js guide
Build an APi in your server. Your clients will make requests to this server to get all todos, and post new todos to the server. Here's an express.js guide
You need a database to store your todos in the server. You can use something like MongoDB or an ORM like Sequelize for node.js.
Now you can either write a code to periodically request the server for todos in the background and update it in your vue components, or you can use a pub/sub library like pusher. Pusher uses WebSockets under the hood for maintaining a persistent bidirectional connection. If you want to, you can implement this on your own, you can read about it here, thanks to #Aurora for the link to the tutorial.
Here's a consolidated guide for doing all this:
https://pusher.com/tutorials/realtime-app-vuejs/
Is there a way to update all clients that view the todos, without clients have to refresh nothing, as soon as a new todo is added in the state, using only vuex/vue?
There's a couple of errors in your code:
change todos:'' to todos:[]
change state.todos.unshift(todo) to state.todos.push(todo)
This way, every time that you call addTodo action, all components connected to allTodos getter will show the latest todos
NOTE:
Vuex/Vue are reactive. So in every page that you see using that showcomponent will show you the last update. If you want to show in every USER CONNECTED, of course you don't need http request, you need WEBSOCKETS

How to write to Redux store outside of a React component?

I have a React Native app where I am using HeadlessJS to call a handler on receipt of a Firebase Cloud Messaging notification.
Inside my handler which is a function, not a React component, I am accessing the Redux store using the following method:
import store from '../redux/store';
const backgroundNotificationHandler = async message => {
const state = store.getState();
...
My question is, how can I update the store in a a way that isn't a 'hack'?
Currently I have the following line in the function:
state.user.wokenFromBackgroundListener = true;
Surprisingly is works, but this is without dispatching an action or using a reducer.
Is there a better way to do it?
I can't dispatch an action because the function is not a component and it is not a component because it can't be - it requires a function as in the docs.
My code in index.js is:
AppRegistry.registerComponent(appName, () => App);
firebase.messaging().setBackgroundMessageHandler(backgroundNotificationHandler);
Dispatching from component props is just an added functionality provided by react-redux. You can still use plain redux api, in this case you can dispatch to store using store.dispatch
store.dispatch({ type: 'SOME_ACTION', payload: [1,2,3] })
However I'm not sure if you should be doing that, I haven't used HeadlessJS myself but I would first check and make sure that these task handlers are actually being run in the same context your app is running (e.g. confirm that they share store instance with your app, and NOT create a separate store just because you import store in file with the handler)

Redux-Persist with React-Native-Background-Fetch

I am creating a React-Native app that fetches data from an API as a background service.
I have looked around the web if I can manually rehydrate the store with the data fetched during the background task, but I could not find anything.
Is it possible to rehydrate the redux-persist store manually while the app is killed, from a background 'service' task?
For the people still wondering, if it is possible to use react-native-background-fetch for scheduling ANY task, it is completely fine as long as it does not touch the UI eg. (AsyncStorage, Redux-Persist, Realm, DB...) is not directly related to invoking change in the UI, so it is completely fine to use.
In my particular case, I am using the slowest option - AsyncStorage - to persist a props sort of object which I use on global App level and pass derived data onto my components:
// Example of HeadlessTask implementation
import BackgroundFetch from 'react-native-background-fetch'
import AsyncStorage from '#react-native-community/async-storage';
const HeadlessTask = async () => {
// Prepare data - fetching from API, other transformations...
let propsObject = {};
AsyncStorage.setItem(ITEM_KEY, JSON.strigify(propsObject))
.then(() => {
console.log('[AsyncStorage] Object Saved!');
// For iOS specifically we need to tell when the service job
// is done.
BackgroundFetch.finish();
})
.catch((e) => {
console.log('[AsyncStorage] Error saving object: ', e);
BackgroundFetch.finish();
});
}
P.S. See https://github.com/transistorsoft/react-native-background-fetch to see how to install and implement Background Fetch.

Issue using redux variables via redux call inside react-apollo graphql with apollo compose

My Component needs to call an graphql query but it needs variables which can be only used by redux. So I have mapped the redux's connect to react apollo compose. But I have issues getting the data from reducer and the call is not loading properly.
class abc extends Component {
componentWillMount() {
console.log(this.props.variableData); // Getting empty object.
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ variableDataFetchAction }, dispatch);
}
function mapStateToProps(state) {
return {
variableData: state.reducerName.variableData,
}
}
export default compose(
connect(mapStateToProps, mapDispatchToProps),
graphql(MyQuery, {
options: props => {
return {
.....,
variables: props.variableData,
};
},
}),
)(abc);
I need to know whether there is problem in the way in which I can redux. Because my store seems to empty.
Just to be clear, you don't need react-apollo to call a GraphQL api endpoint.
In your case, you're utilizing 2 library that handle it's own store (assuming you're using Apollo Client 2). Redux has it's Redux Store and Apollo is using InMemoryCache for caching.
Stuff you fetched via graphql are stored in Apollo cache, you can use Apollo Client Developer Tools Chrome extensions to inspect it's content.
If you want to put the data fetch via apollo-client back to Redux store, you have to manually dispatch an action to do it (once the data return from graphql calls). This is mostly undocumented since it's not recommended to use 2 types of Store.
If all you need is to call a GraphQL endpoint and not changing your Redux implementation. I suggest using apollo-fetch or graphql-request instead.

Vue - Do API calls belong in Vuex?

I am struggling with finding answer for where to ideally put API calls in vue modules. I am not building an SPA. For example my auth block has several components for login, password reset, account verifiction etc. Each block uses axios for API calls. Axios already provides promises, which are async.
The question is about the best pracitces. Do API calls belong in a Vuex actions? Are there any pros/cons of such approach?
Is there any drawback of keeping axios calls within the components they belong to?
I do API calls in services, not Vuex or components. Basically, mixing the API calls in with the store code is a bit too multi-responsibility, and components should be about providing for the view not fetching data.
As an example of a simple service (using Vue.http but same for an Axios call),
FileService .js
import Vue from 'vue'
export default {
getFileList () {
return Vue.http.get('filelist.txt')
.then(response => {
// massage the response here
return filelist;
})
.catch(err => console.error('getFileList() failed', err) )
},
}
I use it in another service as below (the number of layers is up to you).
Note, the outer service is checking the store to see if the fetch already happened.
DataService.js
import FileService from './file.service'
checkFiles (page) {
const files = store.state.pages.files[page]
if (!files || !files.length) {
return store.dispatch('waitForFetch', {
resource: 'files/' + page,
fetch: () => FileService.getFileList(),
})
} else {
return Promise.resolve() // eslint-disable-line no-undef
}
},
waitForFetch is an action that invokes the fetch function passed in to it (as provided by FileService). It basically provides wrapper services to the fetch, like timeout and dispatching success and failure actions depending on the outcome.
The component never knows about the API result (although it may initiate it), it just waits on data to appear in the store.
As for drawback of just calling the API in the component, it depends on testability, app complexity. and team size.
Testability - can mock out a service in unit tests.
App complexity - can handle timeout / success / failure orthogonally to the API call.
Team size - bigger teams, dividing up the task into smaller bites.