I created a Relay.Mutation
that should trigger updates on User
objects:
class UserMutation extends Relay.Mutation {
public getMutation() {
return Relay.QL`mutation {saveUser}`;
}
public getVariables() {
return {
id: this.props.user.id,
loginName: this.props.user.loginName,
firstName: this.props.user.firstName,
lastName: this.props.user.lastName,
mail: this.props.user.mail
}
}
public getFatQuery() {
return Relay.QL`
fragment on UserPayload {
user {
id,
loginName,
firstName,
lastName,
mail
}
}
`;
}
public getConfigs() {
return [{
type: "FIELDS_CHANGE",
fieldIDs: {
user: this.props.user.id
}
}];
}
static fragments = {
user: () => Relay.QL`
fragment on User {
id,
// I initially had only id here
loginName,
firstName,
lastName,
mail
}
`
}
}
I'm using this mutation in my component UserDetails
like this:
// I initially passed this.props.user to the mutation
this.props.relay.commitUpdate(new UserMutation({ user: this.state.user })
When executing, relay passes a user
to the backend with only id
set, without any other property. The mutation is not executed, because the input variable is missing the other fields.
After debugging into the mutation, I saw that this.props.user
has set undefined
on all fields but id. However, this._unresolvedProps.user
is a user
with all fields set correctly.
When I change the code of the mutation and replace all this.props
by this._unresolvedProps
, all necessary data is transmitted to the backend and the mutation is executed without any error. Frontend cache seems to be updated correctly, too (fields like firstName
are updated in other components). But I don`t expect this to be the right way to go.
What do I miss?
UPDATE
The UserDetails
component loads users data like loginName
and provides text boxes to change these properties. The corresponding relay container looks like this:
export default Relay.createContainer(UserDetails, {
fragments: {
user: () => Relay.QL`
fragment on User {
id,
loginName,
firstName,
lastName,
mail,
roles {
id,
name
},
${UserMutation.getFragment("user")}
}
`
}
});
I handle text box changes in a text input handler...
public handleTextInput(fieldName: string, event: any) {
let user = this.state.user;
switch (fieldName) {
case "loginName": {
user.loginName = event.target.value;
break;
}
case "firstName": {
user.firstName = event.target.value;
break;
}
case "lastName": {
user.lastName = event.target.value;
break;
}
case "mail": {
user.mail = event.target.value;
break;
}
}
this.setState({ user: user });
}
...and form submit in a submit handler, where I now pass this.state.user
to the mutation:
public handleSubmit(e: any) {
e.preventDefault();
this.props.relay.commitUpdate(new UserMutation({ user: this.state.user }), {
onSuccess: (response: any) => {
this.setState({ user: response.saveUser.user });
}
});
}
I use a C# backend: graphql-dotnet. This is what I have defined for the mutation:
public class ApplicationSchema : Schema
{
public ApplicationSchema()
{
this.Query = new ApplicationQuery();
this.Mutation = new ApplicationMutation();
}
}
public class ApplicationMutation : ObjectGraphType
{
public ApplicationMutation()
{
this.Name = "Mutation";
// save a user
this.Field<UserPayloadType>(
"saveUser",
arguments: new QueryArguments(
new QueryArgument<NonNullGraphType<UserInputType>>
{
Name = "input",
Description = "the user that should be saved"
}),
resolve: context =>
{
var userInput = context.Argument<UserInput>("input");
var clientMutationId = userInput.ClientMutationId;
var user = MemoryRepository.UpdateUser(new User()
{
Id = userInput.Id,
LoginName = userInput.LoginName,
FirstName = userInput.FirstName,
LastName = userInput.LastName,
Mail = userInput.Mail
});
return new UserPayload()
{
ClientMutationId = clientMutationId,
User = user
};
});
}
}
public class UserInputType : InputObjectGraphType
{
public UserInputType()
{
this.Name = "UserInput";
this.Field<NonNullGraphType<StringGraphType>>("id", "The id of the user.");
this.Field<NonNullGraphType<StringGraphType>>("loginName", "The login name of the user.");
this.Field<NonNullGraphType<StringGraphType>>("firstName", "The first name of the user.");
this.Field<NonNullGraphType<StringGraphType>>("lastName", "The last name of the user.");
this.Field<NonNullGraphType<StringGraphType>>("mail", "The mail adress of the user.");
this.Field<NonNullGraphType<StringGraphType>>("clientMutationId", "react-relay property.");
}
}
public class UserPayloadType : ObjectGraphType
{
public UserPayloadType()
{
this.Name = "UserPayload";
this.Field<NonNullGraphType<UserType>>("user", "The user.");
this.Field<NonNullGraphType<StringGraphType>>("clientMutationId", "react-relay property.");
}
}
public class UserType : ObjectGraphType
{
public UserType()
{
this.Name = "User";
this.Field<NonNullGraphType<StringGraphType>>("id", "The id of the user.");
this.Field<NonNullGraphType<StringGraphType>>("loginName", "The login name of the user.");
this.Field<NonNullGraphType<StringGraphType>>("firstName", "The first name of the user.");
this.Field<NonNullGraphType<StringGraphType>>("lastName", "The last name of the user.");
this.Field<NonNullGraphType<StringGraphType>>("mail", "The mail adress of the user.");
Field<ListGraphType<RoleType>>("roles", resolve: context => MemoryRepository.GetRolesOfUser(context.Source as DomainModel.Models.User));
}
}
saveUser
? Problem might be withsaveUser
. – Ahmad Ferdousfragments
field. – Ahmad Ferdous