mirror of
https://github.com/re-rxjs/react-rxjs.git
synced 2025-12-08 18:01:51 +00:00
feat(utils): add contextBinder
This commit is contained in:
parent
a0b004a5df
commit
56e0643a30
60
packages/utils/src/contextBinder.test.tsx
Normal file
60
packages/utils/src/contextBinder.test.tsx
Normal file
@ -0,0 +1,60 @@
|
||||
import { render, screen } from "@testing-library/react"
|
||||
import React, { createContext, useContext } from "react"
|
||||
import { of } from "rxjs"
|
||||
import { contextBinder } from "./index"
|
||||
|
||||
describe("contextBinder", () => {
|
||||
it("bounds the provided context into the first args of the hook", () => {
|
||||
const idContext = createContext<string>("id1")
|
||||
const countContext = createContext<number>(3)
|
||||
const useId = () => useContext(idContext)
|
||||
const useCount = () => useContext(countContext)
|
||||
const idCountBind = contextBinder(useId, useCount)
|
||||
|
||||
const [useSomething] = idCountBind(
|
||||
(id: string, count: number, append: string) =>
|
||||
of(Array(count).fill(id).concat(append).join("-")),
|
||||
"",
|
||||
)
|
||||
|
||||
const Result: React.FC = () => <span>Result {useSomething("bar")}</span>
|
||||
|
||||
const Component: React.FC<{ id: string; count: number }> = ({
|
||||
id,
|
||||
count,
|
||||
}) => {
|
||||
return (
|
||||
<idContext.Provider value={id}>
|
||||
<countContext.Provider value={count}>
|
||||
<Result />
|
||||
</countContext.Provider>
|
||||
</idContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
render(<Component id="foo" count={4} />)
|
||||
|
||||
expect(screen.queryByText("Result foo-foo-foo-foo-bar")).not.toBeNull()
|
||||
})
|
||||
|
||||
it("the returned function matches the signature of the original one", () => {
|
||||
const idContext = createContext<string>("id1")
|
||||
const countContext = createContext<number>(3)
|
||||
const useId = () => useContext(idContext)
|
||||
const useCount = () => useContext(countContext)
|
||||
const idCountBind = contextBinder(useId, useCount)
|
||||
|
||||
const [, getSomething$] = idCountBind(
|
||||
(id: string, count: number, append: string) =>
|
||||
of(Array(count).fill(id).concat(append).join("-")),
|
||||
"",
|
||||
)
|
||||
|
||||
let value = ""
|
||||
getSomething$("foo", 4, "bar").subscribe((v) => {
|
||||
value = v
|
||||
})
|
||||
|
||||
expect(value).toBe("foo-foo-foo-foo-bar")
|
||||
})
|
||||
})
|
||||
38
packages/utils/src/contextBinder.ts
Normal file
38
packages/utils/src/contextBinder.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { Observable } from "rxjs"
|
||||
import { bind } from "@react-rxjs/core"
|
||||
|
||||
type SubstractTuples<A1, A2> = A2 extends [unknown, ...infer Rest2]
|
||||
? A1 extends [unknown, ...infer Rest1]
|
||||
? SubstractTuples<Rest1, Rest2>
|
||||
: []
|
||||
: A1
|
||||
|
||||
const execSelf = <T>(fn: () => T) => fn()
|
||||
|
||||
/**
|
||||
* Returns a version of bind where its hook will have the first parameters bound
|
||||
* the results of the provided functions
|
||||
*
|
||||
* @param {...React.Context} context - The React.Context that should be bound to the hook.
|
||||
*/
|
||||
export function contextBinder<
|
||||
A extends (() => any)[],
|
||||
OT extends {
|
||||
[K in keyof A]: A[K] extends () => infer V ? V : unknown
|
||||
}
|
||||
>(
|
||||
...args: A
|
||||
): <AA extends any[], T, ARGS extends [...OT, ...AA]>(
|
||||
getObservable: (...args: ARGS) => Observable<T>,
|
||||
defaultValue?: T | undefined,
|
||||
) => [
|
||||
(...args: SubstractTuples<ARGS, OT>) => T,
|
||||
(...args: ARGS) => Observable<T>,
|
||||
]
|
||||
export function contextBinder(...args: any[]) {
|
||||
const useArgs = () => args.map(execSelf)
|
||||
return function () {
|
||||
const [hook, getter] = bind.apply(null, arguments as any) as any
|
||||
return [(...args: any[]) => (hook as any)(...useArgs(), ...args), getter]
|
||||
} as any
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user