reprovider
TypeScript icon, indicating that this package has built-in type declarations

1.1.0-stable • Public • Published

Texto alternativo

Reprovider

A minimalist package created created for manage services using depedency injection easly and quickly.

Overview

When our react app is scaling sometimes is necessary that we organize the logic for get a better understanding of our code.

Reprovider is designed for developers that want get a quickly and organized service provider in a react application using techniques like the Dependency Injection very simple.

With reprovider we can create services and consume into a component or outside of it.

Reprovider supports Redux for inject services into reducers and thunks. If you want use reprovider with redux, Read this section

Full docs

Read the latest full docs here

How it works

Texto alternativo

Quick start

Install

with npm:

    npm i reprovider

Using Reprovider

First create the repository, it can contains the interacions with an A.P.I or Database.

greets.repository.ts

export class GreetsRepository {
  hello = () => "Hello ";
  goodBay = () => "Goodbay ";
}

Now create the service and inject the repository into constructor.

greets.service.ts

import { GreetsRepository } from "../repository/greets.repository";

export class GreetsService {
  constructor(private readonly repository: GreetsRepository) {}
  sayHello = (): string => `${this.repository.hello()} World From Reprovider`;
  sayGoodbye = (): string =>
    `${this.repository.goodBay()} World From Reprovider`;
}

Its time to create the registry file.

It contains all services with its dependencies that will register into ServiceProvider

registry.ts

import { Service } from "reprovider";
import { GreetsService } from "./services/greets.service";
import { GreetsRepository } from "./repository/greets.repository";

export const services: Service[] = [
  {
    description: "Service for greeting people",
    service: GreetsService,
    dependencies: [GreetsRepository],
  },
];

Once do you configure the services and declared into registry file. FInally we can inject the services into the app.

For that, add the ServiceProvider at the top of the app.

The service provided will be responsible of create the context that we will using later calling the useProvider hook.

app.tsx

import { ServiceProvider } from "reprovider";
import SayHello from "./components/say-hello.component";
import { services } from "./core/registry";

const App = (): JSX.Element => {
  return (
    <ServiceProvider services={services}>
      <SayHello />
    </ServiceProvider>
  );
};

export default App;

Say hello with useProvider

Finally, just import the useProvider, pass the reference of Service that you wan use and desestructure the provider.

Optional: You can rename the provider as you want. This is recommended for understand the code, better.

say-hello.component.tsx

import { useProvider } from "reprovider";
import { GreetsService } from "../core/services/greets.service";

/**
 * Show a simple hello world
 *
 */
const SayHello = () => {
  /**
   * Optional: Also we can rename the provider or use the standard reference.
   * Is recommended rename the provider for understand the code better
   */
  const { provider: greetsService } = useProvider(GreetsService);

  return (
    <div>
      <h1>{greetsService.sayHello()}</h1>
    </div>
  );
};

export default SayHello;

Buildux

What is buildux?

Buildux is a slice builder that allows inject services into reducers and thunks creating a context and consume services from reducers easy. It works with redux-toolkit library.

How Buildux Works

You can review the next graph for understand the buildux flow

Texto alternativo

Usage

Install the dependencies

npm install reprovider @reduxjs/toolkit react-redux

First create the state interface

counter-state.interface.ts

export interface CounterState {
  value: number;
}

Now, create the buildux file, import the state, and pass the state type to the instance. This will return a context that you will use later in reducers.

counter.buildux.ts

import { Buildux } from "reprovider";
import { CounterState } from "../../interfaces/counter-state.interface";

const { context } = new Buildux<CounterState>({
  name: "counter",
  initialState: { value: 0 },
});

After created the Buildux instance, we need create the reducers. For that, use the createReducers method:

counter.buildux.ts

const { context } = new Buildux<CounterState>({
  name: "counter",
  initialState: { value: 0 },
}).createReducers({
  reducers: {
    increment: (state) => {
      state.value += 1;
    },
    decrement: (state) => {
      state.value -= 1;
    },
  },
});

then exports the actions created from reducers and the reducer from context:

counter.buildux.ts

export const { increment, decrement } = context.actions;
export const counterReducer = context.reducer;

Now create the store and import the exported reducer from counter.buildux.ts

store.ts

import { configureStore } from "@reduxjs/toolkit";
import { counterReducer } from "./counter-store/counter.buildux";

export const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

In your index.tsx, import the store and the Provider Component and pass the store to tag.

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./index.css";

import { store } from "./core/store/store";
import { Provider } from "react-redux";

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);

For consume the reducers and dispatch actions we need create these hooks:

hooks.ts

import { useDispatch, useSelector } from "react-redux";
import type { TypedUseSelectorHook } from "react-redux";
import { AppDispatch, RootState } from "../store/store";

//Hooks for read and emit states with redux.

//Calls an action
export const useAppDispatch: () => AppDispatch = useDispatch;

//Read the state
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

Now, we are ready to use the states and actions. For that, import the hooks in the component and use the hooks as follows:

app.tsx

const count = useAppSelector((state) => state.counter.value);
const dispatch = useAppDispatch();

app.tsx

import reproviderLogo from "./assets/reprovider_logo.png";
import "./App.css";
import { useAppSelector, useAppDispatch } from "./core/hooks/hooks";
import {
  decrement,
  increment,
} from "./core/store/counter-store/counter.buildux";

function App() {
  const count = useAppSelector((state) => state.counter.value);
  const dispatch = useAppDispatch();
  return (
    <>
      <div>
        <img src={reproviderLogo} className="logo" alt="Reprovider logo" />
        <a href="https://redux-toolkit.js.org/">
          <img
            src="https://redux-toolkit.js.org/img/redux.svg"
            className="logo Redux Toolkit"
            alt="Redux toolkit logo"
          />
        </a>
      </div>
      <h1>Reprovider + Redux Example</h1>
      <div className="card">
        <button onClick={() => dispatch(increment(1))}>+1</button>
        <h1>Counter value is: {count}</h1>
        <button onClick={() => dispatch(decrement(1))}>-1</button>
        <p>
          Edit <code>src/App.tsx</code> and save to test HMR
        </p>
      </div>
    </>
  );
}

export default App;

You can read the complete example reading here

Buildux Thunk

Overview

Buildux provide a custom thunk for asincronus calls and it's an implementation of createAsyncThunk.

Creating a BuilduxThunk

import { builduxThunk } from "reprovider";

builduxThunk<User[], ActionArgument>({
  // You can infer the type of the 'action' parameter here if it has one.
  description: "Get user from API",
  reference: "/users",
  action: async () => {
    const usersService = context.consumer.get(UsersService);
    const users = await usersService.getUsers();
    return users;
  },
});

For use the thunk we need register into thunkRegistry method that it's provided when we create the Buildux instance

users.buildux.ts

const { context, thunksRegistry, createReducers } = new Buildux<
  UsersState,
  UsersThunks
>({
  name: "users",
  services,
  initialState: {
    users: [],
  },
});

createReducers(usersReducers);

thunksRegistry(context, (context) => ({
  fetchUsers: builduxThunk<User[]>({
    description: "Get user from API",
    reference: "/users",
    action: async () => {
      const usersService = context.consumer.get(UsersService);
      const users = await usersService.getUsers();
      return users;
    },
  }),
}));

also we can declare our thunks out of our buildux file and the pass the reference into thunksRegistry.

users-thunks.registry.ts

export const userThunksRegistry = <T>(context: BuilduxContext<T>) => ({
  fetchUsers: builduxThunk<User[]>({
    description: "Get user from API",
    reference: "/users",
    action: async () => {
      const usersService = context.consumer.get(UsersService);
      const users = await usersService.getUsers();
      return users;
    },
  }),
});

then we can import it into buildux file.

users.buildux.ts

const { context, thunksRegistry } = new Buildux<UsersState>({
  name: "users",
  services,
  initialState: {
    users: [],
  },
});
createReducers(usersReducers);

//Imported here
thunksRegistry(context, userThunksRegistry);

Buildux Examples

You can view the examples with buildux and thunks here

Injecting Services and consuming into reducer

If you need consume services into a reducer you can follow the next steps:

Create the registry file for store the services for slice

Recomendation:

It is recommended to create a registry for the slice and only insert the services that are part of it. This helps build a better code structure.

Create the registry file

registry.ts

import { Service } from "reprovider";

export const services: Service[] = [
  {
    description: "Service for greeting people",
    service: UsersService,
    dependencies: [UsersRepository],
  },
];

users.buildux.ts

import { services } from "./registry";

const { context } = new Buildux<UsersState>({
  name: "users",
  services,
  initialState: {
    name: "",
    lastName: "",
    email: "",
  },
}).createReducers({
  getUsers: (state) => {
    const service = context.consumer.get(MyService);
    // Do something with your service...
  },
});

Consuming services into a thunk

export const userThunksRegistry = <T>(context: BuilduxContext<T>) => ({
  fetchUsers: builduxThunk<User[]>({
    description: "Get user from API",
    reference: "/users",
    action: async () => {
      //Read the service from context.
      const usersService = context.consumer.get(UsersService);
      const users = await usersService.getUsers();
      return users;
    },
  }),
});

Examples

You can view more examples follow this link

Package Sidebar

Install

npm i reprovider

Weekly Downloads

2

Version

1.1.0-stable

License

GPL-3.0

Unpacked Size

191 kB

Total Files

51

Last publish

Collaborators

  • hellseher