diff --git a/examples/3-mutation/src/Dog.re b/examples/3-mutation/src/Dog.re index c1575af3..a9e1a13c 100644 --- a/examples/3-mutation/src/Dog.re +++ b/examples/3-mutation/src/Dog.re @@ -1,4 +1,5 @@ open ReasonUrql; +open Hooks; module Mutations = { module LikeDog = [%graphql @@ -21,13 +22,15 @@ module Mutations = { |} ]; - let treatDog = {| + module TreatDog = [%graphql + {| mutation treatDog($key: ID!) { treatDog(key: $key) { treats } } - |}; + |} + ]; module BellyscratchDog = [%graphql {| @@ -52,6 +55,10 @@ let make = ~bellyscratches: int, ~description: string, ) => { + /* useMutation with variables applied at render time. */ + let (_, executeLikeMutation) = + useMutation(~request=Mutations.LikeDog.make(~key=id, ())); + let payload = React.useMemo1( () => { @@ -62,17 +69,9 @@ let make = [|id|], ); - let (_, executeLikeMutation) = - Hooks.useMutation(~request=Mutations.LikeDog.make(~key=id, ())); - - let (_, executeTreatMutation) = - Hooks.useMutation( - ~request={ - "query": Mutations.treatDog, - "variables": payload, - "parse": x => x, - }, - ); + /* useMutation functor API, allowing variables to be applied at execution time. */ + module TreatDogMutation = MakeMutation(Mutations.TreatDog); + let (_, executeTreatMutation) = TreatDogMutation.useMutation();
name @@ -98,7 +97,7 @@ let make = emoji={j|🍖|j} count={string_of_int(treats)} hex="7b16ff" - onClick={_ => executeTreatMutation() |> ignore} + onClick={_ => executeTreatMutation(Some(payload)) |> ignore} /> ...{({executeMutation}) => diff --git a/src/hooks/UrqlUseMutation.re b/src/hooks/UrqlUseMutation.re index 91698e92..c3484a19 100644 --- a/src/hooks/UrqlUseMutation.re +++ b/src/hooks/UrqlUseMutation.re @@ -54,10 +54,63 @@ let useMutation = (~request) => { ); let executeMutation = - React.useCallback1( + React.useCallback2( () => executeMutationJs(Some(request##variables)), - [|request##variables|], + (executeMutationJs, request##variables), ); (response, executeMutation); }; + +/** + * The functor implementation of useMutation. Useful for when you want to + * apply variables at execution time rather than at render time. + * + * Accepts the following arguments: + * + * Mutation - a graphql_ppx or graphql_ppx_re module containing the + * type t of the GraphQL mutation, the query string of the GraphQL mutation, + * and a parse function for decoding the JSON response. + */ +module type MutationConfig = { + type t; + let query: string; + let parse: Js.Json.t => t; +}; + +module type MakeMutationType = + (Mutation: MutationConfig) => + { + let useMutation: + unit => + ( + UrqlTypes.hookResponse(Mutation.t), + React.callback( + option(Js.Json.t), + Js.Promise.t(UrqlClient.ClientTypes.operationResult), + ), + ); + }; + +module MakeMutation: MakeMutationType = + (Mutation: MutationConfig) => { + let useMutation = () => { + let (responseJs, executeMutationJs) = useMutationJs(Mutation.query); + + let response = + React.useMemo2( + () => responseJs |> urqlResponseToReason(Mutation.parse), + (Mutation.parse, responseJs), + ); + + let executeMutation = + React.useCallback1( + variables => executeMutationJs(variables), + [|executeMutationJs|], + ); + + (response, executeMutation); + }; + + useMutation; + }; diff --git a/src/hooks/UrqlUseMutation.rei b/src/hooks/UrqlUseMutation.rei index d5e20019..63f542b5 100644 --- a/src/hooks/UrqlUseMutation.rei +++ b/src/hooks/UrqlUseMutation.rei @@ -4,3 +4,23 @@ let useMutation: UrqlTypes.hookResponse('response), unit => Js.Promise.t(UrqlClient.ClientTypes.operationResult), ); + +module type MutationConfig = { + type t; + let query: string; + let parse: Js.Json.t => t; +}; + +module type MakeMutationType = + (Mutation: MutationConfig) => + { + let useMutation: + unit => + ( + UrqlTypes.hookResponse(Mutation.t), + option(Js.Json.t) => + Js.Promise.t(UrqlClient.ClientTypes.operationResult), + ); + }; + +module MakeMutation: MakeMutationType; diff --git a/src/hooks/UrqlUseQuery.re b/src/hooks/UrqlUseQuery.re index c0be8a2f..6b7060f9 100644 --- a/src/hooks/UrqlUseQuery.re +++ b/src/hooks/UrqlUseQuery.re @@ -2,6 +2,7 @@ [@bs.deriving abstract] type useQueryArgs = { query: string, + [@bs.optional] variables: Js.Json.t, [@bs.optional] requestPolicy: UrqlTypes.requestPolicy, @@ -93,3 +94,68 @@ let useQuery = (~request, ~requestPolicy=?, ~pause=?, ()) => { (response, executeQuery); }; + +/** + * The functor implementation of useQuery. An alternative to the function API. + * + * Accepts the following arguments: + * + * Query - a graphql_ppx or graphql_ppx_re module containing the + * type t of the GraphQL query, the query string of the GraphQL query, + * and a parse function for decoding the JSON response. + */ +module type QueryConfig = { + type t; + let query: string; + let parse: Js.Json.t => t; +}; + +module type MakeQueryType = + (Query: QueryConfig) => + { + let useQuery: + ( + ~variables: Js.Json.t=?, + ~requestPolicy: UrqlTypes.requestPolicy=?, + ~pause: bool=?, + unit + ) => + ( + UrqlTypes.hookResponse(Query.t), + React.callback( + option(UrqlClient.ClientTypes.partialOperationContext), + unit, + ), + ); + }; + +module MakeQuery: MakeQueryType = + (Query: QueryConfig) => { + let useQuery = (~variables=?, ~requestPolicy=?, ~pause=?, ()) => { + let args = + useQueryArgs( + ~query=Query.query, + ~variables?, + ~requestPolicy?, + ~pause?, + (), + ); + let (responseJs, executeQueryJs) = useQueryJs(args); + + let response = + React.useMemo2( + () => responseJs |> urqlResponseToReason(Query.parse), + (Query.parse, responseJs), + ); + + let executeQuery = + React.useCallback1( + opts => executeQueryJs(opts), + [|executeQueryJs|], + ); + + (response, executeQuery); + }; + + useQuery; + }; diff --git a/src/hooks/UrqlUseQuery.rei b/src/hooks/UrqlUseQuery.rei index 34ff2e63..81e0dd83 100644 --- a/src/hooks/UrqlUseQuery.rei +++ b/src/hooks/UrqlUseQuery.rei @@ -14,3 +14,30 @@ let useQuery: unit ) => useQueryResponse('response); + +module type QueryConfig = { + type t; + let query: string; + let parse: Js.Json.t => t; +}; + +module type MakeQueryType = + (Query: QueryConfig) => + { + let useQuery: + ( + ~variables: Js.Json.t=?, + ~requestPolicy: UrqlTypes.requestPolicy=?, + ~pause: bool=?, + unit + ) => + ( + UrqlTypes.hookResponse(Query.t), + React.callback( + option(UrqlClient.ClientTypes.partialOperationContext), + unit, + ), + ); + }; + +module MakeQuery: MakeQueryType; diff --git a/src/hooks/UrqlUseSubscription.re b/src/hooks/UrqlUseSubscription.re index 33fdf132..ad6a6be2 100644 --- a/src/hooks/UrqlUseSubscription.re +++ b/src/hooks/UrqlUseSubscription.re @@ -95,3 +95,63 @@ let useSubscription = (handler, args, parse), ); }; + +/** + * The functor implementation of useQuery. An alternative to the function API. + * + * Accepts the following arguments: + * + * Query - a graphql_ppx or graphql_ppx_re module containing the + * type t of the GraphQL query, the query string of the GraphQL query, + * and a parse function for decoding the JSON response. + */ +module type SubscriptionConfig = { + type t; + let query: string; + let parse: Js.Json.t => t; +}; + +module type MakeSubscriptionType = { + type resp; + + let useSubscription: + (~variables: Js.Json.t=?, ~handler: handler('acc, resp, 'ret)) => + UrqlTypes.hookResponse('ret); +}; + +module MakeSubscription = + (Subscription: SubscriptionConfig) + : (MakeSubscriptionType with type resp = Subscription.t) => { + type resp = Subscription.t; + + let useSubscription = + (type acc, type ret, ~variables=?, ~handler: handler(acc, resp, ret)) + : UrqlTypes.hookResponse(ret) => { + let args = + useSubscriptionArgs(~query=Subscription.query, ~variables?, ()); + + React.useMemo3( + () => { + let response: UrqlTypes.hookResponse(ret) = + switch (handler) { + | Handler(handlerFn) => + useSubscriptionJs( + args, + Some( + (acc, data) => handlerFn(acc, Subscription.parse(data)), + ), + )[0] + |> useSubscriptionResponseToRecord(x => x) + | NoHandler => + useSubscriptionJs(args, None)[0] + |> useSubscriptionResponseToRecord(Subscription.parse) + }; + + response; + }, + (handler, args, Subscription.parse), + ); + }; + + useSubscription; +}; diff --git a/src/hooks/UrqlUseSubscription.rei b/src/hooks/UrqlUseSubscription.rei index 944275bd..ede72466 100644 --- a/src/hooks/UrqlUseSubscription.rei +++ b/src/hooks/UrqlUseSubscription.rei @@ -8,3 +8,21 @@ let useSubscription: ~handler: handler('acc, 'resp, 'ret) ) => UrqlTypes.hookResponse('ret); + +module type SubscriptionConfig = { + type t; + let query: string; + let parse: Js.Json.t => t; +}; + +module type MakeSubscriptionType = { + type resp; + + let useSubscription: + (~variables: Js.Json.t=?, ~handler: handler('acc, resp, 'ret)) => + UrqlTypes.hookResponse('ret); +}; + +module MakeSubscription: + (Subscription: SubscriptionConfig) => + MakeSubscriptionType with type resp = Subscription.t;