Using Variables with react-apollo Query - javascript

I've attached a query to my React Native component like so:
let getNearbyStoriesQuery = gql`{
getStoriesNearbyByGeoHash(geoHash: "8k"){
id
}
}`;
export default graphql(getNearbyStoriesQuery,
{
props: (props) => {
debugger;
let storiesNearby = props.data.getStoriesNearbyByGeoHash.map((story) => {
return {
id: story.id,
}
});
return {storiesNearby};
},
variables: {
geoHash: mockGeoHash
}
})(Home); // Home is the React Native Component
I'm able to retrieve data from using this technique as long as I hardcode a value in for geoHash in the query; in this case I used "8k". However, when I attempt to modify the query so that I can puss in a variable like so:
let getNearbyStoriesQuery = gql`{
getStoriesNearbyByGeoHash($geoHash: String!){
id
}
}`;
I get an error saying Expected Name, found $. This method of passing variables to queries is repeated across multiple sources. What am I doing wrong here?

Should be:
query GetStoriesNearbyByGeoHash($geoHash: String!) {
getStoriesNearbyByGeoHash(geoHash: $geoHash){
id
}
}
Have a look on how to use variables in graphql: http://graphql.org/learn/queries/#variables

Related

How to obtain the `initialQueryRef` in Relay?

In the Relay docs, to fetch a query you have to first preload it using useQueryLoader, and then later pass the result of this into usePreloadedQuery. However, this first step, useQueryLoader takes two arguments: a query itself, which is easy to obtain, and preloadedQueryReference, which the docs do not explain how to obtain. All they say is " e.g. provided by router", but no router actually supports Relay with hooks so this is not helpful information.
As a simple example of this, I have a single component where I want to preload and then use a query:
import {
usePaginationFragment,
usePreloadedQuery,
useQueryLoader
} from "react-relay";
import graphql from 'babel-plugin-relay/macro';
const MY_QUERY = graphql` some stuff here `;
export default function SomeComponent(props) {
const initialRef = ???????;
const [
queryReference,
loadQuery,
disposeQuery,
] = useQueryLoader(AllHits, initialRef);
const qry = usePreloadedQuery(AllHits, queryReference);
}
However without the initialRef, I can't proceed any further. How do I obtain this?
It seems that the query ref is returned from useQueryLoader or loadQuery. The argument to useQueryLoader (initialRef in the original post) is optional, and might be derived from a previous call to loadQuery done by the router, but it in no way required. An example of using useQueryLoader which loads the query as soon as possible is this:
const some_query = graphql` some query here`;
function Parent(props){
// Note, didn't pass in the optional query ref here
const [
queryRef,
loadQuery,
disposeQuery
] = useQueryLoader(some_query);
// Load immediately
useEffect(() => {
loadQuery(
{count: 20},
{fetchPolicy: 'store-or-network'},
);
}, [loadQuery]);
if (!!queryRef) {
return <Child queryRef={queryRef}/>;
} else {
return "loading...";
}
}
export function Child(props) {
const {queryRef} = props;
const loaded = usePreloadedQuery(some_query, queryRef);
const { data } = useFragment(
graphql`some_fragment`,
loaded
);
return JSON.stringify(data);
}

how to create apollo client query with custom types

I have to do a query and I have to pass a nested variable.
Below is the working query when I use apollo graphql client interface. It is giving me expected result. Below is the working query
query($input: MyProductInput!){
MyProductCategories(input: $input){
id,
name
}
}
Variable which i am passing
{
"input": {
"locale": "ENG"
}
}
MyProductInput type look like this at SERVER
type MyProductInput {
locale: Locale!
}
enum Locale {
IND
AUS
ENG
}
when I try to call the same query from my React App, it is giving me error, It says 400 bad request. My React query look like this.
const PRODUCT_LIST = gql`
query ($locale: String!) {
MyProductCategories(input: {locale: $locale}){
id,
name
}
}
`;
const { loading, error, data } = useQuery(PRODUCT_LIST, {
variables: {
"input": {
"locale": "ENG"
}
},
});
How can i convert my react query to accommodate custom types??
Note: I am using JavaScipt not Typescript at Front-end side
I got the answer. I just had to do this $input: MyProductInput! within query and pass $input to MyProductCategories. Below is working example.
const PRODUCT_LIST = gql`
query ($input: MyProductInput!) {
MyProductCategories(input: $input){
id,
name
}
}
`;
React Query using hooks
const { loading, error, data } = useQuery(PRODUCT_LIST, {
variables: {
"input": {
"locale": "ENG"
}
},
});
No need to worry about custom types as it is already there at server. Just pass is as it is and set the variables
Take a look in the docs:
https://www.apollographql.com/docs/react/api/react/hooks/
And can you see more information about the error message you get back?

Default string value after call the object in JavaScript

I have a js object in which I return my endpoint addresses from api. This is a very nice solution for me, it looks like this:
export const API_BASE_URL = 'http://localhost:3000';
export const USERS = '/Users';
export default {
users: {
checkEmail: (email) => `${API_BASE_URL}${USERS}/${email}/checkEmail`,
notifications: `${API_BASE_URL}${USERS}/notifications`,
messages: `${API_BASE_URL}${USERS}/messages`,
},
};
Now I can call this address in my redux-saga to execute the xhr query:
import { api } from 'utils';
const requestURL = api.users.notifications;
But I'm a bit stuck because now I have a problem - base path is missing here: '/users'.
Now when I call api.users, then I get a object. I would like to have a default value after calling the object like:
import { api } from 'utils';
const requestURL = api.users; // http://localhost:3000/Users
const requestURL2 = api.users.notifications; // http://localhost:3000/Users/notifications
I know that I could add a new string with the name 'base' to the object and add '/Users' there, but I don't like this solution and I think, there is a better solution.
You could do one of the following:
extend the String class
const API_BASE_URL = "http://localhost:3000"
const USERS = "/Users"
class UsersEndpoints extends String {
constructor(base) {
super(base)
}
// this is still a proposal at stage 3 to declare instance variables like this
// if u want a truly es6 way you can move them to the constructor
checkEmail = (email) => `${API_BASE_URL}${USERS}/${email}/checkEmail`
notifications = `${API_BASE_URL}${USERS}/notifications`
messages = `${API_BASE_URL}${USERS}/messages`
}
// you can use userEndpoints itself as a string everywhere a string is expected
const userEndpoints = new UsersEndpoints(API_BASE_URL)
export default {
users: userEndpoints
}
The previous is just actually equivalent to
...
const userEndpoints = new String(API_BASE_URL)
userEndpoints.notifications = `${API_BASE_URL}${USERS}/notifications`
...
Obviously this is not recommended: you should not extend native classes, there are many disadvantages to this approach.
An obvious example is that there could be a conflict between the properties you use and the properties that might be brought by the native class
override the toString method
...
export default {
users: {
checkEmail: (email) => `${API_BASE_URL}${USERS}/${email}/checkEmail`,
notifications: `${API_BASE_URL}${USERS}/notifications`,
messages: `${API_BASE_URL}${USERS}/messages`,
toString: () => API_BASE_URL
},
};
// this is actually not much different than the previous method, since a String is an objet with an overridden toString method.
// That said this method is also not recommended since toString is used in many places in native code, and overriding it just to substitute a string value will make information get lost in such places, error stacks for example
Achieve what u want using the language features intended for such a use case
What you are asking is to make the same variable to have different values in the same time, which is not possible in the language syntax, and it makes sense because it makes it hard to reason about code.
that being said i recommend something of the following nature
// it is also better to use named exports
export const getUsersEndpoint = ({
path = "",
dynamicEndpointPayload = {},
} = {}) => {
switch (path) {
case "notifications":
return `${API_BASE_URL}${USERS}/notifications`
case "messages":
return `${API_BASE_URL}${USERS}/messages`
case "checkEmail":
return `${API_BASE_URL}${USERS}/${dynamicEndpointPayload.email}/checkEmail`
// you still can do checkEmail like this, but the previous is more consistent
// case "checkEmail":
// return (email) => `${API_BASE_URL}${USERS}/${email}/checkEmail`
default:
return `${API_BASE_URL}`
}
}
// you can use it like this
getUsersEndpoint() // returns the base
getUsersEndpoint({path: 'notifications'})
You can extend prototype to achieve this behaviour:
export const API_BASE_URL = 'http://localhost:3000';
export const USERS = '/Users';
const users = `${API_BASE_URL}${USERS}`
const baseUrls = {
checkEmail: (email) => `${users}/${email}/checkEmail`,
notifications: `${users}/notifications`,
messages: `${users}/messages`,
}
Object.setPrototypeOf(users.__proto__, baseUrls);
export default {
users
};
Try having object will all user endpoint and a function that return a value of a end point
const user = {
default: '/users',
notification: '/notification',
profile: '/profile',
getEndPoint(prop) {
if(this[prop] === 'default' ){
return this[prop];
} else {
if(this[prop]) {
return this.default + this[prop];
}
}
}
}
So you can have more end points that come under user and you can simply call
const requestURL = api.user.getEndPoint('default'); // http://localhost:3000/Users
const requestURL2 = api.user.getEndPoint('notifications'); // http://localhost:3000/Users/notification

What is the meaning of this strange syntax [SOME_MUTATION] (state) for function definition in JavaScript?

In Vue I see code like:
// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
and
// store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'
const store = new Vuex.Store({
state: { ... },
mutations: {
// we can use the ES2015 computed property name feature
// to use a constant as the function name
[SOME_MUTATION] (state) {
// mutate state
}
}
})
Where the syntax [SOME_MUTATION] (state) comes from? Is [SOME_MUTATION, ANOTHER] (state) also valid syntax? Why SOME_MUTATION (state) (which is valid function syntax) is not used?
How in large Vue project, putting all export const SOME_MUTATION = 'SOME_MUTATION' defintions in separate file really help instead of putting in same file (store.js) ?
In this line
import { SOME_MUTATION } from './mutation-types'
where you are importing the types into SOME_MUTATION, it has a string value. Now to define a function with this name inside an object you need to do
{
[SOME_MUTATION](state) {
}
}
Would be same as (if SOME_MUTATION has a value say changeState)
{
changeState: function(state){
}
}
And state is by default passed as an argument in mutations.
Read more here
We prefer this type of architecture in a Vue application so as to avoid name conflicts in defining Mutations and Actions and Getters across multiple components.
It's a javascript syntax for defining an object key programatically/dynamically.
for example, you have a variable:
var myFunctionName = "getUsers";
you can actually create a function inside an object using the value of a variable as the function name.
So for example,
{
[myFunctionName]() {
// do something
}
}
will actually become:
ES6:
{
getUsers() {
// do something
}
}
or ES5:
{
getUsers: function() {
// do something
}
}
This is not limited to creating functions within an object, but it can be applied to any property within your object.
For example:
let myPropertyName = 'user';
{
[myPropertyName]: {
name: 'Hello',
age: 10
}
}
will become:
{
user: {
name: 'Hello',
age: 10
}
}

Dispatch Redux action after React Apollo query returns

I'm using React Apollo to query all records in my datastore so I can create choices within a search filter.
The important database model I'm using is Report.
A Report has doorType, doorWidth, glass and manufacturer fields.
Currently when the query responds, I'm passing allReports to multiple dumb components which go through the array and just get the unique items to make a selectable list, like so..
const uniqueItems = []
items.map(i => {
const current = i[itemType]
if (typeof current === 'object') {
if (uniqueItems.filter(o => o.id !== current.id)) {
return uniqueItems.push(current)
}
} else if (!uniqueItems.includes(current)) {
return uniqueItems.push(current)
}
return
})
Obviously this code isn't pretty and it's a bit overkill.
I'd like to dispatch an action when the query returns within my SidebarFilter components. Here is the query...
const withData = graphql(REPORT_FILTER_QUERY, {
options: ({ isPublished }) => ({
variables: { isPublished }
})
})
const mapStateToProps = ({
reportFilter: { isPublished }
// filterOptions: { doorWidths }
}) => ({
isAssessment
// doorWidths
})
const mapDispatchToProps = dispatch =>
bindActionCreators(
{
resetFilter,
saveFilter,
setDoorWidths,
handleDoorWidthSelect
},
dispatch
)
export default compose(connect(mapStateToProps, mapDispatchToProps), withData)(
Filter
)
The Redux action setDoorWidths basically does the code above in the SidebarFilter component but it's kept in the store so I don't need to re-run the query should the user come back to the page.
It's very rare the data will update and the sidebar needs to change.
Hopefully there is a solution using the props argument to the graphql function. I feel like the data could be taken from ownProps and then an action could be dispatched here but the data could error or be loading, and that would break rendering.
Edit:
Query:
query ($isPublished: Boolean!){
allReports(filter:{
isPublished: $isPublished
}) {
id
oldId
dbrw
core
manufacturer {
id
name
}
doorWidth
doorType
glass
testBy
testDate
testId
isAssessment
file {
url
}
}
}
While this answer addresses the specific issue of the question, the more general question -- where to dispatch a Redux action based on the result of a query -- remains unclear. There does not, as yet, seem to be a best practice here.
It seems to me that, since Apollo already caches the query results in your store for you (or a separate store, if you didn't integrate them), it would be redundant to dispatch an action that would also just store the data in your store.
If I understood your question correctly, your intent is to filter the incoming data only once and then send the result down as a prop to the component's stateless children. You were on the right track with using the props property in the graphql HOC's config. Why not just do something like this:
const mapDataToProps = ({ data = {} }) => {
const items = data
const uniqueItems = []
// insert your logic for filtering the data here
return { uniqueItems } // or whatever you want the prop to be called
}
const withData = graphql(REPORT_FILTER_QUERY, {
options: ({ isPublished }) => ({
variables: { isPublished }
}),
props: mapDataToProps,
})
The above may need to be modified depending on what the structure of data actually looks like. data has some handy props on it that can let you check for whether the query is loading (data.loading) or has errors (data.error). The above example already guards against sending an undefined prop down to your children, but you could easily incorporate those properties into your logic if you so desired.

Categories

Resources