fhooks
v0.0.2
Published
Dependency Injection for functions via hooks
Downloads
5
Readme
fhooks
Dependency Injection for functions via hooks
About
In OO-programming we have multiple ways for dependency injection: constructor injection, method injection, decorators, etc. But, for functional programming it is always proceed via arguments, because we want to have pure function, cleaned from side effects. But, sometimes we want to use functions with side effects and not pass arguments through all tree of calls. So, i wrote this library, inspired on React-hooks. For to use it, we should change our codebase onetime, every function should be wrapped in special format, but next time, when you will need for additional injection, you will just add it very simply.
For example, we have some function:
function A() {
doSomething1();
doSomething2();
}
And we want to add log between two lines:
function A() {
doSomething1();
log("Some logs...");
doSomething2();
}
But, we don't want global function log
, we want use some Logger
, like Winston, singleton for all project. Now, we should pass logger as argument to function.
function A(logger) {
doSomething1();
logger.log("Some logs...");
doSomething2();
}
It means, we need to change function-interface and pass logger every time, when we want to call function.
Now, we use fhooks
and change function, like this:
const LoggerContext = createContext();
const A = ({ useContext }: ContextInjector) => () => {
const logger = useContext(LoggerContext);
doSomething1();
logger.log("Some logs...");
doSomething2();
};
In future, if we will need extra dependency, we will not change the function's signature, all dependencies will pass by useContext
.
Now, we can test our function use a testing or development Context
Install
npm install fhooks --save
or
yarn add fhooks
Usage
import { createContextExecutor, createContext, ContextInjector } from "fhooks";
const StringContext = createContext<string>();
const LoggerContext = createContext<typeof logger>();
// init executor
const executor = createContextExecutor();
// create some logger
const globalLogger = {
log: (...args: any[]) => console.log(...args),
};
executor.provideContext(LoggerContext, globalLogger);
// function A
const A = ({ call, useContext }: ContextInjector) => () => {
const logger = useContext(LoggerContext);
logger.log("Run function A");
return call(B, "fromA");
};
// function B
const B = ({ call, useContext }: ContextInjector) => (param1: string) => {
const logger = useContext(LoggerContext);
logger.log("Run function B");
const stringValue = useContext(StringContext);
return "returnFromB::" + param1 + "::" + stringValue;
};
const App = ({ call, useContext, provideContext }) => () => {
const logger = useContext(LoggerContext);
logger.log("Run App");
provideContext(StringContext, "SomeString");
return call(A);
};
const result = executor.run(App);
// "returnFromB::fromA::SomeString"
API
class ContextExecutor {
provideContext = <T>(context: Context<T>, value: T): void;
run<F extends (...args: any[]) => any>(fn: InjectableFunction<F>, ...args: Parameters<F>): ReturnType<F>;
}
class ContextInjector {
call = <F extends (...args: any[]) => any>(fn: InjectableFunction<F>, ...args: Parameters<F>): ReturnType<F>;
provideContext = <T>(context: Context<T>, value: T): void;
useContext = <T>(context: Context<T>): T;
}
class Context<T = any> {
}
function createContext<T>(params: IContextParams<T> = {}): Context<T>;
Test
npm install
npm test