Is it possible to cancel a graphql query inside config.options? - redux

Let's say I have this portion of code:
export default graphql(MyQuery, {
options: (props) => ({
variables: {
var1: props.var1,
var2: props.var2,
var3: props.var3
/* I want to do this:
if (props.var4 == "Donot query") /* Cancel this query; */
*/
}
})
})(DumbComponent)
Is it possible to do this?
Reason is my props is injected by Redux store. Some of the query needs a combination of changes to var1 & var2 for example to fetch the correct data. However, sometimes some action will change only var1 in Redux, and that will trigger the graphql query and yield incorrect data. For those cases, I want to cancel the query.
I know this problem can be solved by design. Meaning we can create a set of variables required for the query separately in Redux, and any action touches this set, need to touch individual in the set.
However, doing so will require Redux to be aware of specific graphql query, creating a tight coupling.

Depending on what you mean by "cancel the query", you might be able to use the skip option:
export default graphql(MyQuery, {
skip: (props) => !! props.var4,
options: (props) => ({
variables: {
var1: props.var1,
var2: props.var2,
var3: props.var3
}
})
})(DumbComponent)
See the docs for that here: http://dev.apollodata.com/react/api-graphql.html#graphql-config-skip

You can refer this StackOverflow answer
I have made a POC that shed's light on - how to cancel Apollo GraphQL pending request. Modify the source code to fit your requirement.
Happy Coding!

Related

Change a mutation value when fetching another query RTK Query?

I have a query that fetches all data, and also have a mutation that handles delete an item
const {
isFetching: isFetchingProducts,
data: productsData,
refetch
} = useGetProductsQuery("");
const [
deleteProduct,
{ isSuccess: productDeleted, error: deleteProductFailed }
] = useDeleteProductMutation();
I'm trying to make productDeleted false when isFetchingProducts is true, we could achieve that in old redux by for example
case(GET_PRODUCTS)
isFetching: true,
productDeleted: false
How to do that in the RTK query? thanks
Those two exist completely independently from each other, so there is no real way of doing that.
We will be adding a .reset functionality at some point in the future (github issue here), but currently you will just have to track something like that in a local component state variable.

does redux-query-sync enable sharing url between machines?

I am trying to implement redux-query-sync but the url keeps going to default state if I share the url which has the updated state.
https://github.com/Treora/redux-query-sync/blob/master/src/redux-query-sync.js
I have implemented as shown in the sample - https://codesandbox.io/s/url-query-sync-qjt5f?from-embed=&file=/src/index.js
There is also a PropsRoute implementation in the code.
Your example seems to be working for the string arguments. It's the array param selected which is giving you trouble.
The action creator that you are dispatching here, changeLocation, does not take the right arguments. Look at how you are calling it in your component:
changeLocation({
location: event.target.name,
checked: event.target.checked
});
When it is called from the URL query it is going to be called like:
changeLocation([Stockholm, Oslo]);
So obviously these do not match up. The existing changeLocation action can only handle one location at a time, so there's not a way to map the value from the URL to a payload that you can use with it. You will need to create a separate action.
const setLocation = (payload) => ({ type: "setLocation", payload });
case "setLocation":
return {...state, selected: payload};
My first approach to handle the array values was to implement the optional stringToValue and valueToString settings for the selected param.
This only kinda-sorta works. I can't get it to omit the param from the URL when it is set to the default value. I wonder if it's using a === check? As a hacky solution, I added a filter in the stringToValue to prevent the empty string from getting added to the locations array. But the selected= is always present in the URL.
selected: {
selector: (state) => state.selected,
action: setLocation,
stringToValue: (string) => string.split(",").filter(s => !!s),
valueToString: (value) => value.join(","),
defaultValue: []
}
This really annoyed me, so I followed this bit of advice from the docs:
Note you could equally well put the conversion to and from the string in the selector and action creator, respectively. The defaultValue should then of course be a string too.
And this approach worked much better.
selected: {
selector: (state) => state.selected.join(","),
action: (string) => setLocation(string.split(",")),
defaultValue: ""
}
With those changes you should have a shareable URL.
Forked Sandbox
Thanks Linda. The example I sent was what I referred to do my implementation. It wasn't my work.
Just letting you know that since my app uses PropsRoute I was able to use history props to push the state as params in url and share the url. I had to modify code to use params from url as state if it was available. This worked between tabs. Will be testing across machines.
this.props.history.push("/currenturl/" + state)
this.props.history.push("/currenturl/" + {testobject:{}})
this.props.match.params.testobject
wasn't able to implement redux-query-sync though.

Is it a good idea to nest constants in Redux?

In our Product we use Angular 6 together with NgRX 6. Instead of defining our constants as export const strings, we use an object to encapsulate them:
export const ACTION_CONSTANTS = {
'OPEN_MODAL' : 'OPEN_MODAL',
'CLOSE_MODAL' : 'CLOSE_MODAL',
'OPEN_TOOLTIP' : 'OPEN_TOOLTIP',
'CLOSE_TOOLTIP' : 'CLOSE_TOOLTIP',
...
};
As the ACTION_CONSTANTS object gets bigger and prefixes get longer ('DROPDOWN_ACTION_SKIP_SET_INIT_STATE'), I would prefer to nest constants e.g. by feature:
export const ACTION_CONSTANTS = {
'MODAL' : {
'OPEN' : 'MODAL.OPEN',
'CLOSE' : 'MODAL.CLOSE'
},
'TOOLTIP' : {
'OPEN' : 'TOOLTIP.OPEN',
'CLOSE' : 'TOOLTIP.CLOSE'
},
...
};
Is it a good idea or are there any downsides? I could not find anything on formatting constants on the Redux FAQ.
I don't think it is a bad idea, as long as you're able to keep it all organized. But I would suggest grouping your actions into different files. I find this the best way to keep things organized.
--ActionsFile
-modalActions.js
-toolTipAction.js
I usually keep actions in different files, roughly aligned with models & reducers. And i have a naming convention like:
ACTION_MODEL_OUTCOME
So, for example, to load model of type ProductGroup i would have actions:
export const ActionTypes = {
LOAD_PRODUCTGROUP: enforceUnique("[ProductGroup] Laod ProductGroup"),
LOAD_PRODUCTGROUP_SUCCESS: enforceUnique("[ProductGroup] Load ProductGroup Success")
LOAD_PRODUCTGROUP_FAILURE: enforceUnique("[ProductGroup] Load ProductGroup Failure")
}
enforceUnique is a function that caches all registered actions and make sure there are no duplicates across the whole app.
Now, when you import actions for certain model, you import only those from file you need (e.g. import ProductGroupActionTypes from 'actions/ProductGroupActions') and use them like ProductGroupActionTypes.LOAD_PRODUCTGROUP.
Usually, first one (without outcome suffix) is the one to initiate action and set some pending flag in reducer to show loader and also to initiate http calls in #Effects.
Second one, with success suffix is handled in reducer to change state.
Third one is error handling, whatever way you want to do it.

Access other slice of state in reducer using #ngrx/store

Given the following (and assuming we cannot change the state's structure):
StoreModule.forRoot({
a: aReducer,
b: {
b1: b1Reducer,
b2: b2Reducer
}
});
and b1Reducer is dependent on the value of a (for example because it contains something like user info).
What is the most idiomatic way to access (read-only) a in b1Reducer?
The solution I came up with is using #ngrx/effects, dispatch another action with a that can be used in the reducer:
#Effect()
augmentAction$ = this.action$
.ofType(Actions.Action1)
.withLatestFrom(this.store$)
.switchMap(([action, state]:[Action, AppState]) => {
const a = state.a;
return [new Actions.Action2(a)];
});
This works, but it becomes hard to manage if almost every action needs to be redispatched if a is used in many reducers. Is there a better way to handle this?

How to use normalized state in Redux?

I need help designing a simple app which allows user to rate videos using a form. My state is composed by 2 reducers, one that holds data about all ratable videos (in a normalized fashion) and another one that holds the form state:
{
videos: {
'video1Id': { id: 'video1Id', title: 'Cat video', duration: 120, ... },
'video2Id': { ... },
...
},
rateForm: {
'videoId': 'video1Id'
'userComment: 'A nice video about cat'
'formSubmitted': false
...
}
}
Note that, inside rateForm, I reference the video id instead of the video object. Problem is, how can I retreive the whole video object from my rateForm reducer ?
I feel like I'm following the best practice of Redux design but I'm stuck at this really simple use case. Any help appreciated.
Thanks
One thing to remember, reducer should be AS SIMPLE AS POSSIBLE. Only doing atomic operations on reducer level. From what I can tell you trying to retrieve the whole video object in your reducer just doesn't sound right.
Depending on your needs, usually, you don't need to fetch the whole video object if you just want to comment on it or rate it. But if you are 100% sure you have to, A good place to do this is in your action. Using Redux-Thunk, you will have access to the whole state object before you return your thunk. Example
function doSomethingToVideo (videoId, something) {
return (dispatch, getState) => {
const video = getState().videos[videoId]
// Do what ever
return somethingElse
}
}
Reference: Redux author's answer on a similar matter.
Accessing Redux state in an action creator?

Resources