Multiple Mutations on a single object - relayjs

I try to have a addColumn and a removeColumn mutation on a Chart component.
But when I call
Relay.Store.commitUpdate(new RemoveChartColumnMutation({
chart: this.props.viewer.chart,
column: this.props.viewer.chart.columns[0]
})
I get this error
"Fragment \"F2\" cannot be spread here as objects of type "AddChartColumnPayload\" can never be of type \"RemoveChartColumnPayload\"."
What am I doing wrong here?
export default Relay.createContainer(Home, {
fragments: {
viewer: () => Relay.QL`
fragment on User {
chart {
columns
${AddChartColumnMutation.getFragment('chart')}
${RemoveChartColumnMutation.getFragment('chart')}
}
}`
}
});
With these mutations
class AddChartColumnMutation extends Relay.Mutation {
getMutation() {
return Relay.QL`mutation {addChartColumn}`;
}
getVariables() {
return {
id: this.props.chart.id,
key: this.props.key,
aggregation: this.props.aggregation
};
}
getFatQuery() {
return Relay.QL`
fragment on AddChartColumnPayload {
chart {
columns
}
}
`;
}
getConfigs() {
return [{
type: 'FIELDS_CHANGE',
fieldIDs: {
chart: this.props.chart.id
}
}];
}
static fragments = {
chart: () => Relay.QL`
fragment on Chart {
id
type
}
`
};
}
and
class RemoveChartColumnMutation extends Relay.Mutation {
getMutation() {
return Relay.QL`mutation {addChartColumn}`;
}
getVariables() {
return {
chartId: this.props.chart.id,
columnId: this.props.column.id
};
}
getFatQuery() {
return Relay.QL`
fragment on RemoveChartColumnPayload {
chart {
columns
}
}
`;
}
getConfigs() {
return [{
type: 'FIELDS_CHANGE',
fieldIDs: {
chart: this.props.chart.id
}
}];
}
static fragments = {
chart: () => Relay.QL`
fragment on Chart {
id
type
}
`
};
}

I think you've just got a typo in your code. Looks like both mutations are calling the addChartColumn mutation, which would explain the error expecting the remove chart column payload.

Related

How to force refetch the entire node in Relay?

Suppose that we have this mutation:
const [addCryptoAddress] =
useMutation(graphql`
mutation useCrypto_AddCryptoWalletAddressMutation(
$input: AddCryptoAddressInput!
) {
addCryptoAddress(input: $input) {
userAccount {
cryptoCurrencyAddresses {
count
edges {
node {
id
address
}
}
}
}
errors {
message
}
}
}
`);
If successful, a new CryptoCurrencyAddresses will be available under UserAccount.
Now suppose that somewhere else in the code we have a lazyLoadQuery that fetches these addresses, e.g.
const { visitor } = useLazyLoadQuery<ManualPayoutMachineContextQuery>(
graphql`
query ManualPayoutMachineContextQuery {
visitor {
userAccount {
cryptoCurrencyAddresses {
edges {
node {
id
address
canDelete
currency
isDefault
label
}
}
}
}
}
}
`,
{}
);
However, note that this query references additional fields that are not mentioned in the mutation. The result is that all unmentioned fields are undefined immediately after the mutation, i.e.
visitor.userAccount?.cryptoCurrencyAddresses.edges
.map(({ node }) => {
return {
address: node.address,
canDelete: node.canDelete,
currency: node.currency,
id: node.id,
isDefault: node.isDefault,
label: node.label,
};
});
Produces:
[
{
address: '0xc0ffee254729296a45a3885639AC7E10F9d54979',
canDelete: undefined,
currency: undefined,
id: 'WyJDcnlwdG9DdXJyZW5jeUFkZHJlc3MiLDIwMjE3NF0=',
isDefault: undefined,
label: undefined,
}
]
Apart from listing every overlapping field in the mutation, is there a way to force all queries that depend on this data to reload when they detect new data?
You can set fetchPolicy as store-and-network, like this:
const { visitor } = useLazyLoadQuery<ManualPayoutMachineContextQuery>(
graphql`
query ManualPayoutMachineContextQuery {
visitor {
userAccount {
cryptoCurrencyAddresses {
edges {
node {
id
address
canDelete
currency
isDefault
label
}
}
}
}
}
}
`,
{},
{
fetchPolicy: 'store-and-network',
}
);
The store-and-network will use the data of the store and always do a network request without making regardless.

Why is `doesFilterPass` not being called in this custom filter

It's my understanding that when filterChangedCallback is called that the grid filters the current data and calls doesFilterPass and isFilterActive as it does so.
Here I have column that uses a custom filter, and a floating filter. The floating filter (a checkbox) gets displayed ok, and when I click on it onFloatingFilterChanged in the custom filter is called, the green filter active icon appears, and the grid refreshes, but doesFilterPass does not get called at all and I can't figure out why.
There's something fundamental I'm not getting with custom filters so if anyone can shed any light on this I'd be grateful.
(Edit: it may be the interplay between the custom/floating filters that's at issue. There was a similar ag-grid-angular issue posted to github last year but nothing came of that. So perhaps it's a bug?)
Custom filter
export default class BooleanFilter {
init(params) {
this.params = params;
this.valueGetter = params.valueGetter;
this.filterChangedCallback = params.filterChangedCallback;
this.status = false;
}
onFloatingFilterChanged(status) {
this.status = status;
this.filterChangedCallback();
}
getGui() {
return '<div />';
}
getModel() {
return this.isFilterActive() ? { filterType: 'boolean', filter: this.status } : undefined;
}
setModel(model) {
this.state.status = model ? model.value : '';
}
isFilterActive() {
return this.status === true;
}
doesFilterPass(params) {
console.log(this.status, params);
}
}
Floating filter
export default class FloatingCheckboxFilter extends Component {
state = { status: false };
handleToggle = (e) => {
const { target: { checked } } = e;
const { parentFilterInstance } = this.props;
this.setState({ status: checked });
parentFilterInstance((instance) => {
instance.onFloatingFilterChanged(checked);
});
}
onParentModelChanged = (parentModel) => {
this.setState({
status: !parentModel ? false : parentModel.filter
});
}
render() {
const { status } = this.state;
return (
<input
style={{ marginTop: '0.75em' }}
type="checkbox"
checked={status}
onChange={this.handleToggle}
/>
);
}
}

Modern Relay - ConnectionConfig for multiple connections

EDIT: after some research I found out that ReactRelayPaginationContainer does only support one #connection for now (relay release 1.0.0).
I am not sure how to deal with multiple Connections in a PaginationContainer using Relay Modern as the only examples I saw so far only included one Connection. To illustrate my concern, there is some code below where I am having troubles
export default createPaginationContainer(MyComponent,
{
item: graphql`fragment mycomponent_item on Item {
foos(first: $count_foo, after: $cursor_foo) #connection(key: item_foos) {
pageInfo {
hasNextPage
endCursor
}
edges {
node {
id
...foo_foo
}
}
}
bars(first: $count_bar, cursor: $cursor_bar) #connection(key: item_bars) {
pageInfo {
hasNextPage
endCursor
}
edges {
node {
id
...bar_bar
}
}
}
}
}`
},
{
direction: 'forward',
getConnectionFromProps(props) {
return props.item.foos && props.item.bars;
},
getFragmentVariables(prevVars, totalCount) {
return {
...prevVars,
count: totalCount,
};
},
getVariables(props, {count, cursor}, fragmentVariables) {
return {
count,
cursor,
};
},
query: graphql`
query itemPaginationQuery(
$count_foo: Int!
$cursor_foo: String
$count_bar: Int!
$cursor_bar: String
) {
Item {
... mycomponent_item
}
}
`
});
The problem is in the ConnectionConfig of PaginationContainer of Relay Modern:
export type ConnectionConfig = {
direction?: 'backward' | 'forward',
getConnectionFromProps?: (props: Object) => ?ConnectionData,
getFragmentVariables?: FragmentVariablesGetter,
getVariables: (
props: Object,
paginationInfo: {count: number, cursor: ?string},
fragmentVariables: Variables,
) => Variables,
query: GraphQLTaggedNode,
};
It is clearly made just for one Connection in the PaginationContainer. And createPaginationContainer only accepts one of such ConnectionConfigs in it's constructor.
How am I supposed to deal with multiple Connections in one PaginationContainer?

How to specify edges with arguments in mutation's fat query

According to this answer it should be possible to specify edges with arguments in fat query, but I can't make it work. Here are is my mutation config and fat query:
getFatQuery() {
return Relay.QL`
fragment on AddCommentPayload {
commentEdge(sort:[{createdAt: ASC}])
customer {
id
comments
}
}
`;
}
getConfigs() {
return [{
type: 'RANGE_ADD',
parentName: 'customer',
parentID: this.props.customer.id,
connectionName: 'comments',
edgeName: 'commentEdge',
rangeBehaviors() {
return 'append';
},
}];
}
Problem is that resulting query doesn't contain any arguments on edge field:
mutation AddComment($input_0:AddCommentInput!) {
addComment(input:$input_0) {
clientMutationId,
...F1
}
}
fragment F0 on Customer {
id
}
fragment F1 on AddCommentPayload {
commentEdge {
cursor,
__typename,
node {
id,
createdAt,
commentText
}
},
customer {
id,
...F0
}
}

RelayMutation expects prop to be data fetched by Relay, adding mutation to query not working

I am getting the error:
bundle.js:28169 Warning: RelayMutation: Expected prop `group` supplied to `AddBlock` to be data fetched by Relay. This is likely an error unless you are purposely passing in mock data that conforms to the shape of this mutation's fragment.
It might seem similar to the problem described in this question, but the answer (of making sure the mutation is added to the initial query) is not working as a solution for me. I already have the mutation in the original query.
Here is my relevant code:
export class AddBlock extends Relay.Mutation {
getMutation() {
return Relay.QL`mutation { addBlock }`;
}
getVariables() {
return {
body: this.props.body
};
}
getFatQuery() {
return Relay.QL`
fragment on AddBlock {
newBlockEdge,
group {
blocks
}
}
`;
}
getConfigs() {
return [{
type: 'RANGE_ADD',
parentName: 'group',
parentID: this.props.group.id,
connectionName: 'blocks',
edgeName: 'newBlockEdge',
rangeBehaviors: {
'': 'append',
},
}];
}
getOptimisticResponse() {
return {
newBlockEdge: {
node: {
body: this.props.body
}
},
group: {
id: this.props.group.id
}
}
}
static get fragments() {
return {
group: () => Relay.QL`
fragment on GroupNode {
id
}
`,
}
}
}
class Designer extends React.Component {
...
addToBlocks(blocks) {
// Create a mutation to save to the blocks.
Relay.Store.commitUpdate(
new AddBlock({
body: blocks[0].block,
group: this.props.group
})
);
}
...
}
Designer = Relay.createContainer(Designer, {
fragments: {
group: (Component) => Relay.QL`
fragment on GroupNode {
title
visibility
blocks(first: 20) {
edges {
node {
${Block.getFragment('block')}
${UpdateBlockBodyMutation.getFragment('block')}
position
title
}
}
}
${AddBlock.getFragment('group')}
}
`,
}
});
What could I be doing wrong here?
I suspect your mutation fragment isn't actually being used - you should run a test to see that if you add other fields to the AddBlock fragment, you'll find that they aren't being requested...? I'm not 100% sure why (likely something about static get fragments), but not quite sure.
Once you get your mutation fragment actually being used, Relay won't complain anymore since it'll be getting data in the correct way :D

Resources