0.0.2 • Published 5 years ago

fhooks v0.0.2

Weekly downloads
2
License
ISC
Repository
github
Last release
5 years ago

fhooks

Dependency Injection for functions via hooks

NPM version Build Status Dependency Status Coverage percentage experimental

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