feat(utils): add contextBinder

This commit is contained in:
Josep M Sobrepere 2021-01-21 14:23:02 +01:00
parent a0b004a5df
commit 56e0643a30
2 changed files with 98 additions and 0 deletions

View 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")
})
})

View 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
}