Skip to content

Commit

Permalink
feat: fixed useMutationState
Browse files Browse the repository at this point in the history
  • Loading branch information
mbret committed Dec 3, 2023
1 parent adca1f6 commit d9acf2b
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 85 deletions.
12 changes: 11 additions & 1 deletion src/lib/queries/client/mutations/Mutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ export class Mutation<Data> {
getDefaultMutationState()
)

/**
* @important
* proxy for rect-query, not really useful
*/
state: MutationState<Data> = this.stateSubject.getValue()

options: MutationOptions<Data, any>

mutation$: Observable<MutationResult<Data>>
Expand All @@ -40,6 +46,9 @@ export class Mutation<Data> {
this.options = options
const mutationFn = options.mutationFn

this.state.variables = args
this.stateSubject.next(this.state)

const mutationFnObservable =
typeof mutationFn === "function"
? defer(() => from(mutationFn(args)))
Expand Down Expand Up @@ -88,7 +97,8 @@ export class Mutation<Data> {
).pipe(
mergeResults,
tap((value) => {
this.stateSubject.next({ ...this.stateSubject.getValue(), ...value })
this.state = { ...this.state, ...value }
this.stateSubject.next(this.state)
}),
(options.__queryRunnerHook as typeof identity) ?? identity,
share()
Expand Down
4 changes: 2 additions & 2 deletions src/lib/queries/client/mutations/MutationClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ import {
type MutationObservedResult,
type MutationFilters,
type MutationKey,
type Mutation,
MutationState
type MutationState
} from "./types"
import { type QueryKey } from "../keys/types"
import { isDefined } from "../../../utils/isDefined"
import { createMutationRunner } from "./createMutationRunner"
import { createPredicateForFilters } from "./filters"
import { shallowEqual } from "../../../utils/shallowEqual"
import { type Mutation } from "./Mutation"

export class MutationClient {
/**
Expand Down
2 changes: 1 addition & 1 deletion src/lib/queries/client/mutations/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export interface MutationFilters<TData> {
/**
* Match mutation key exactly
*/
exact?: boolean
// exact?: boolean
/**
* Include mutations matching this predicate function
*/
Expand Down
114 changes: 61 additions & 53 deletions src/lib/queries/react/mutations/useMutationState.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
sleep
} from "../../../../tests/utils"
import { useMutation } from "./useMutation"
import { MutationState } from "../../client/mutations/types"
import { type MutationState } from "../../client/mutations/types"
import { memo } from "react"

describe("useMutationState", () => {
describe("types", () => {
Expand All @@ -34,56 +35,63 @@ describe("useMutationState", () => {
})
})
})
// it('should return variables after calling mutate', async () => {
// const queryClient = createQueryClient()
// const variables: unknown[][] = []
// const mutationKey = ['mutation']

// function Variables() {
// variables.push(
// useMutationState({
// filters: { mutationKey, status: 'pending' },
// select: (mutation) => mutation.state.variables,
// }),
// )

// return null
// }

// function Mutate() {
// const { mutate, data } = useMutation({
// mutationKey,
// mutationFn: async (input: number) => {
// await sleep(150)
// return 'data' + input
// },
// })

// return (
// <div>
// data: {data ?? 'null'}
// <button onClick={() => mutate(1)}>mutate</button>
// </div>
// )
// }

// function Page() {
// return (
// <div>
// <Variables />
// <Mutate />
// </div>
// )
// }

// const rendered = renderWithClient(queryClient, <Page />)

// await waitFor(() => rendered.getByText('data: null'))

// fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))

// await waitFor(() => rendered.getByText('data: data1'))

// expect(variables).toEqual([[], [1], []])
// })

it("should return variables after calling mutate", async () => {
const queryClient = createQueryClient()
const variables: unknown[][] = []
const mutationKey = ["mutation"]

const VariablesMemo = memo(function Variables() {
const value = useMutationState({
filters: { mutationKey, status: "pending" },
select: (mutation) => mutation.state.variables
})

variables.push(value)

return null
})

function Mutate() {
const { mutate, data } = useMutation({
mutationKey,
mutationFn: async (input: number) => {
await sleep(150)
return "data" + input
}
})

return (
<div>
data: {data ?? "null"}
<button
onClick={() => {
mutate(1)
}}
>
mutate
</button>
</div>
)
}

function Page() {
return (
<div>
<VariablesMemo />
<Mutate />
</div>
)
}

const rendered = renderWithClient(queryClient, <Page />)

await waitFor(() => rendered.getByText("data: null"))

fireEvent.click(rendered.getByRole("button", { name: /mutate/i }))

await waitFor(() => rendered.getByText("data: data1"))

expect(variables).toEqual([[], [1], []])
})
})
25 changes: 13 additions & 12 deletions src/lib/queries/react/mutations/useMutationState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import { useObserve } from "../../../binding/useObserve"
import { useQueryClient } from "../Provider"
import {
type MutationState,
type MutationFilters,
type Mutation
type MutationFilters
} from "../../client/mutations/types"
import { useLiveRef } from "../../../utils/useLiveRef"
import { type Mutation } from "../../client/mutations/Mutation"
import { skip } from "rxjs"

export interface MutationStateOptions<TResult> {
filters?: MutationFilters<TResult>
Expand All @@ -21,16 +22,16 @@ export const useMutationState = <TResult = MutationState>({
const filtersRef = useLiveRef(filters)

Check failure on line 22 in src/lib/queries/react/mutations/useMutationState.ts

View workflow job for this annotation

GitHub Actions / Release

'filtersRef' is assigned a value but never used
const selectRef = useLiveRef(select)

const { value$, lastValue } = useMemo(
() =>
queryClient.mutationClient.mutationState({
select: (mutation) =>
selectRef.current
? selectRef.current(mutation)
: (mutation.stateSubject.getValue() as TResult)
}),
[queryClient]
)
const { value$, lastValue } = useMemo(() => {
const { lastValue, value$ } = queryClient.mutationClient.mutationState({
select: (mutation) =>
selectRef.current
? selectRef.current(mutation)
: (mutation.stateSubject.getValue() as TResult)
})

return { lastValue, value$: value$.pipe(skip(1)) }
}, [queryClient])

return useObserve(value$) ?? lastValue
}
49 changes: 33 additions & 16 deletions src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable new-cap */
import { memo, useCallback, useState } from "react"
import { memo, useState } from "react"
import ReactDOM from "react-dom/client"
import {
QueryClient,
Expand All @@ -13,10 +13,9 @@ import { usePersistSignals } from "./lib/state/persistance/usePersistSignals"
import { createLocalStorageAdapter } from "./lib/state/persistance/adapters/createLocalStorageAdapter"
import {
QueryClient as rc_QueryClient,
QueryClientProvider as RcQueryClientProvider,
QueryClientProvider as RcQueryClientProvider
// useMutation as rc_useMutation
} from "@tanstack/react-query"
import { useIsMutating } from "./lib/queries/react/mutations/useIsMutating"
import { sleep } from "./tests/utils"
import { useMutationState } from "./lib/queries/react/mutations/useMutationState"

Expand All @@ -28,26 +27,38 @@ const myState = signal({
})

const IsMutating = memo(() => {
const isMutating = useIsMutating({
predicate: useCallback(
({ options: { mutationKey } }: any) => mutationKey[0] === "mutation1",
[]
)
})
// const isMutating = useIsMutating({
// // select: (mutation) => mutation.
// })

console.log("isMutating", isMutating)
// console.log("isMutating", isMutating)

console.log("useMutationState", useMutationState())
console.log(
"useMutationState",
useMutationState({
select(mutation) {
return mutation.state.variables
}
})
)

return null
})

const Mutation = memo((_: { onClick: () => void }) => {
const { mutate: mutate1, reset } = useMutation({
const {
mutate: mutate1,
cancel,
...rest

Check failure on line 52 in src/main.tsx

View workflow job for this annotation

GitHub Actions / Release

'rest' is assigned a value but never used
} = useMutation({
mutationKey: ["mutation1"],
mutationFn: async () => {
await sleep(100)
return "data"
mapOperator: "merge",
mutationFn: async (v: string) => {
await sleep(500)
return v
},
onSuccess: (v) => {
console.log("onSuccess", v)
}
})
const { mutate: mutate2 } = useMutation({

Check failure on line 64 in src/main.tsx

View workflow job for this annotation

GitHub Actions / Release

'mutate2' is assigned a value but never used
Expand Down Expand Up @@ -81,13 +92,19 @@ const Mutation = memo((_: { onClick: () => void }) => {
// console.log("rc", result2)
// console.log("observe", observeMut)

// console.log("mutate", rest)

return (
<div style={{ display: "flex", border: "1px solid red" }}>
mutation
<IsMutating />
<button
onClick={() => {
mutate1()
mutate1("data1")
// mutate1("data2")
// mutate1("data3")
// mutate1("data4")
// cancel()
// reset()
// mutate2()
// result2.mutate({ res: 3, timeout: 1000 })
Expand Down

0 comments on commit d9acf2b

Please sign in to comment.