简体   繁体   English

如何处理中继突变中未解决的道具?

[英]How to handle unresolved props in relay mutations?

I created a Relay.Mutation that should trigger updates on User objects: 我创建了一个Relay.Mutation ,它应该触发User对象上的更新:

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: 我在组件UserDetails使用这种突变,如下所示:

// 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. 执行时,中继将user仅设置了id身份传递到后端,没有任何其他属性。 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. 在调试了突变之后,我看到this.props.user在除id之外的所有字段上都设置了undefined However, this._unresolvedProps.user is a user with all fields set correctly. 但是, this._unresolvedProps.user是正确设置所有字段的user

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. 当我更改变异的代码并将所有this.props替换为this._unresolvedProps ,所有必要的数据都将传输到后端,并且变异得以执行而没有任何错误。 Frontend cache seems to be updated correctly, too (fields like firstName are updated in other components). 前端缓存似乎也已正确更新(诸如firstName类的字段在其他组件中已更新)。 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. UserDetails组件加载诸如loginName类的用户数据,并提供文本框来更改这些属性。 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: ...然后在提交处理程序中提交表单,现在我将this.state.user传递给该this.state.user

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 . 我使用C#后端: 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));
    }
}

Is your Relay container fetching the User fragment correctly? 您的中继容器是否正确提取User片段? I see in your static fragments definition fragment on User is only id field, so I wonder if your parent Relay component is fetching them all. 我在您的static fragments定义中看到用户唯一ID片段是字段,所以我想知道您的父Relay组件是否正在全部获取它们。

Since your mutation is really dependent on those fields, add them to fragments property. 由于您的突变实际上取决于这些字段,因此请将它们添加到fragments属性。

class UserMutation extends Relay.Mutation {
    public getMutation() { ... }

    // getVariables, FatQuery and Configs ...

    static fragments = {
      user: () => Relay.QL`
          fragment on User {
              id,
              loginName,
              firstName,
              lastName,
              mail
          }
      `
    }
}

And then try including this fragment in Relay component, which uses your mutation. 然后尝试将此片段包含在使用您的突变的Relay组件中。 Example React-Relay component: 示例React-Relay组件:

import UserMutation from 'mutations/user';

class User extends Component {
  commit(e) {
    Relay.Store.commitUpdate(
      new UserMutation({
        user: this.props.user
      })
    );
  }

  render() {
    return (
      <div>Hello</div>
    );
  }
};

export default Relay.createContainer(User, {
  fragments: {
    user: () => Relay.QL`
      fragment on User {
        ${UserMutation.getFragment('user')}
      }
    `,
  }
});

Use REQUIRED_CHILDREN and update state in the component. 使用REQUIRED_CHILDREN并更新组件中的状态。

Rather than use FIELDS_CHANGE you could use REQUIRED_CHILDREN which will enable you to add the returned saved object into your store. 您可以使用REQUIRED_CHILDREN而不是使用FIELDS_CHANGE,这将使您可以将返回的已保存对象添加到商店中。 What you would do is set up your getConfigs like this: 您将要做的是像这样设置getConfigs:

getConfigs() {
  return [{
    type: 'REQUIRED_CHILDREN',
      children: [
        Relay.QL`
          fragment on UserPayload {
            user {
              id
              loginName
              firstName
              lastName
              mail
            }
          }
        `
      ]
  }]
}

And in change your commitUpdate like this: 并更改您的commitUpdate,如下所示:

this.props.relay.commitUpdate(
  new UserMutation({user: this.props.user}),
  {
    onSuccess: response => this.setState({
      user: response.user,
    }),
    onError: err => console.log(err)
  }
);

As you can see, the onSuccess callback enables you to call an actionCreator and put the new user into the state of your application. 如您所见,onSuccess回调使您可以调用actionCreator并将新用户置于应用程序状态。 This you would do using whatever state management you are using in your application. 您将使用应用程序中使用的任何状态管理来执行此操作。 In this case it is simply setState. 在这种情况下,它就是setState。

REQUIRED_CHILDREN config is used to append additional children to the mutation query. REQUIRED_CHILDREN配置用于将其他子项附加到突变查询中。 You may need to use this, for example, to fetch fields on a new object created by the mutation (and which Relay would normally not attempt to fetch because it has not previously fetched anything for that object). 例如,您可能需要使用它来获取由该突变创建的新对象上的字段(并且中继通常不会尝试获取该对象,因为该对象以前没有为该对象获取任何东西)。

Data fetched as a result of a REQUIRED_CHILDREN config is not written into the client store, but you can add code that processes it in the onSuccess callback that you pass into commitUpdate() 由于REQUIRED_CHILDREN配置而获取的数据未写入客户端存储,但是您可以在传递给commitUpdate()的onSuccess回调中添加处理该数据的代码。

There is more information in the documentation about REQUIRED_CHILDREN here. 在此处,有关REQUIRED_CHILDREN的文档中有更多信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM