How do I refetch a query outside of its wrapped component? - apollo

I have two screens:
Screen1: Results
Screen2: Edit Filters
When I edit the filters on Screen2 and press back, I would like to refetch the query on Screen1 (with the newly built filter string variable). Editing the filters doesn't use a mutation or fire any Redux actions (I'm storing the users search filters/preferences in localStorage/AsyncStorage instead of a database, so no mutation). I'm merely changing the local state of the form and use that to build a filter string that I want to pass to a certain query on Screen1. I have access to the filter string on both screens if that helps.
It seems like refetch() is limited to the component its query wraps http://dev.apollodata.com/react/receiving-updates.html#Refetch so how would I re-run the query from a different screen?
I tried putting the same query on both Screen1 and Screen2, then calling the refetch on Screen2, and although the query works and gets the new data on Screen2, the same name query doesn't update on Screen1 where I actually need it. Isn't it supposed to if they have the same name? (but the filters variable changed)
If I am just designing this incorrectly and there is an easier way to do it, please let me know. I expect that if I have 2 screens, put the same query on both of them, and refetch one of the queries with a new filters variable, then the refetch should happen in both places, but it's currently treating them individually.

I did the same thing here. The scenario:
- I choose a peer to filter some messages.
- I keep the peerId into redux
- I make both components (the filter and the list) dependent on that redux value.
Like this:
1 - To put that filter value on redux (and to grab it back):
import { compose, graphql } from 'react-apollo'
import { connect } from 'react-redux';
...
export default compose(
connect(
(state,ownProps) => ({
selectedMessages: state.messages.selectedMessages,
peerId: state.messages.peerId
}),
(dispatch) => ({
clearSelection: () => dispatch(clearSelection()),
setPeer: (peerId) => dispatch(setPeer(peerId))
})
),
graphql(
PEERS_QUERY,
...
when you call connect first (using compose), before you call a graphql wrapper, or outside that wrapper, you will have peerId available as a prop on your graphql wrapper, so you can use it to filter your query:
export default compose(
connect(
(state,ownProps) => {
return {
peerId: state.messages.peerId,
selectedMessages: state.messages.selectedMessages
}
},
(dispatch) => ({
toggleMessage(messageId) {
dispatch(toggleMessage(messageId));
}
})
),
graphql( // peerId is available here because this is wrapped by connect
MESSAGES_QUERY,
{
options: ({peerId}) => ({variables:{peerId:peerId}}),
skip: (ownProps) => ownProps.peerId === '',
props: ({
...
...
...
)(MessageList);

Related

Ember hasMany relationship save on create

I'm trying to create an ember data record with a hasMany relationship from a POJO, and it's been remarkably difficult. This is using ember-concurrency (hence the task and yield syntax.
saveEntry: task(function* (obj){
let entry = yield this.model.get('entries').createRecord({
groupId: obj.objectID,
name: obj.name,
...
}).save();
obj.owner_ids.forEach((user_id) => {
this.store.findRecord('user', user_id).then((user) => {
entry.get('owners').pushObject(user);
entry.save();
});
})
}).drop();
So the tough bits are the 'obj.owner_ids', which is an array of user_ids. I add each to the array, but then need to save after every pushObject or it doesn't work. And even when it does work, it is throwing a bunch of network errors (probably due to race conditions on the save). There must be a better way, but I haven't found it.
I'm not sure what this.model represents in your code, but you could try fetching all of the users you need and then attaching them at the time you create and save like:
import { all } from 'rsvp';
saveEntry: task(function* (obj){
let users = obj.owner_ids.map(userId => this.store.findRecord('user', user_id));
let promises = this.model.get('entries').createRecord({
groupId: obj.objectID,
name: obj.name,
owners: users,
...
}).save()
// all() waits for an an array of promises to return
yield all(promises);
}).drop();

Can't Programatically fetch data with Apollo Client

Most of the information out there about Apollo Client and GraphQL queries is about fetching data and immediately rendering something.
What about the common use case where I want to fetch data to, let say, update the state in which I clearly don't need to render JSX, I just want to run Javascript code.
Use the following code snippet as an example
onRefChange (formValues) {
let { project, ref } = formValues
let projectFound = find(this.state.projects, (o) => { return o.id === project.value } )
let variables = {
slug: projectFound.slug, ref: parseInt(ref)
}
console.info('variables ready', variables)
return (
<Query query={RESOLVE_REF} variables={variables}>
{ ({ data, error }) => {
console.info('data response', data)
console.info('error response', error)
return data
}}
</Query>
)
}
Apollo forces me to use the Query component just to perform a query, even when I don't want to render anything. Also those console.info never log anything, but the variables ready text does appear.
I have found that the documentation is pretty clear on using the Query component, but obscure on every option which is different. I feel I'm missing something.
I'm also concerned about how Apollo doesn't seems respect the separation of responsibilities, apparently merging both data and presentation into a single responsibility (as is clear with the Query component), which in my current understanding is quite silly, but most likely I'm fucking things up.
Any insight is appreciated.
As long as you've configured and included an ApolloProvider at the top of your component tree, you can get your query instance using either the withApollo HOC, or the ApolloConsumer:
const MyComponent = ({ client }) => {
// use it!
}
withApollo(MyComponent)
<ApolloConsumer>
{client => (
// use it!
)}
</ApolloConsumer>
You can then use any of the methods that are available to the client instance, including query and mutation, both of which return a Promise that resolves to an ApolloQueryResult object that includes data and errors. The full documentation for the client's API can be found here. Your code would then look something like:
async onRefChange (formValues) {
let { project, ref } = formValues
let projectFound = find(this.state.projects, (o) => { return o.id === project.value } )
let variables = {
slug: projectFound.slug, ref: parseInt(ref)
}
try {
const { data } = await this.props.client(RESOLVE_REF, { variables })
} catch (e) {
// Handle errors
}
}

How to force Apollo Client to use cached data

We have an application that initially load a list of widgets:
query Widgets() {
widgets() {
...Widgets
}
}
fragment Widgets on Widgets {
name
description
rootWidget
widgets {
...WidgetInterface
}
}
fragment WidgetInterface on WidgetInterface {
id
name
description
type
}
later on I render this widgets, where every react component is wrapped with another graphql call to get the data for a single widget. As we fetch this data initially I would expect apollo get the data from local store, but it always make the server call
#import '../fragments/WidgetInterface.graphql'
query Widget($id: ID!) {
widgetDetails(id: $id) {
...WidgetInterface
}
}
So is there away to check why apollo not uses the cached ones?
Apollo caches your query results by query. The reason it's grabbing the data from the server instead of the cache is that the first time you render your component, you've never made a widgetDetails query, only a widgets one.
If you want to avoid making the widgetDetails query, you can set up your component to use the widgets query instead and just filter the results by id yourself. Something like:
graphql(WIDGETS_QUERY, {
props: ({data, ownProps}) => ({ widget: data.widgets.filter(w => w === ownProps.widgetId) })
})(MyComponent)

Continuous data update via pouchdb query

i have a pouchdb database with a number of views setup that i query whenever i need data. I am using observables to handle the querying. However i have to refresh the interface to view any data changes in the database. Is there any way i can have these data changes read directly by the observable ? My code is as:-
home.ts
this.postsService.getPosts().subscribe((posts) => {
this.posts = posts.rows.map(row => {
console.log(row.value);
return row.value;
});
});
posts.ts
getPosts(): Observable<any> {
return Observable.fromPromise(this.db.query('app/inputs'));
}
You can use use db.changes with your view, so that you'll only get events for view related changes:
db.changes({
filter: '_view',
view: 'app/inputs',
live: true,
since: 'now',
include_docs:true
}).on('change', (change) => { this.handleChange(change); });
See the filtered changes section on PouchDB docs for a more detailed explanation on this.

Using Backbone.js in SaaS without templates

I am designing a SaaS application and have been directed to Backbone.js. The service in part tracks DOM events such as how many of each have occurred and then applies scores based on this information.
Decoupling data into Models and Collections is very appealing, but before I go any deeper I want to enquire as to whether it is the right tool for the job.
I want to work with existing DOM elements written in the HTML of a site owners page rather than create JavaScript templates. I will therefore be tracking DOM events on existing elements which then update the data model. The site owner making use of the service will then be able to use the data in the Model to create their own Views and render their own templates specific to their needs.
I understand that I will need to use Backbone.View to track the events, and from what I have read so far it seems Backbone has the flexibility to allow this. However, I haven’t seen any examples in my research of Backbone used to track a bunch of events on a number of form elements.
Take this code for example:
App.Models.Event = Backbone.Model.extend({
defaults: {
clicks: 0,
dblClicks: 0,
tabs: 0,
kbdFunctions: 0
},
urlRoot: 'events'
});
App.Views.Event = Backbone.View.extend({
model: new App.Models.Event(),
events: {
'click input' : 'clickCount',
'dblclick input' : 'dblClickCount',
'tabEvent input' : 'tabCount',
'kbdEvent input' : 'kbdEventCount'
},
initialize: function () {
this.el = $('[data-transaction=start]');
},
clickCount: function (e) {
console.log('click counted');
},
dblClickCount: function (e) {
console.log('double click counted');
},
tabCount: function (e) {
console.log('tab counted');
},
kbdEventCount: function (e) {
console.log('keyboard event counted');
}
});
I want to be able to track clicks, double clicks, tabs and other custom keyboard events that occur on input, textarea, select options and button that are contained within the [data-transaction=start] element. Firstly, is this an applicable use case for Backbone, and secondly, if so what is the best way of adding multiple elements within the Backbone.View events object literals? I haven't seen any examples of this in the documentation or anywhere else, but it would be good if I could add a variable into this like:
...
var someVariable = input, textarea, select, button;
events: {
'click someVariable' : 'clickCount',
...
Events are assigned by Backbone using the delegateEvents method in view. This method is called AFTER your view initialize method (code reference)
so you could pass your variables in view constructor
myView = new App.Views.Events ( someVariable )
in your initialize method, you can assign events:
initialize: function(someVariable) {
//assign this.events from someVariable as you would like
}
EDIT:
just read in Backbone documentation:
The events property may also be defined as a function that returns an
events hash, to make it easier to programmatically define your events,
as well as inherit them from parent views.