inversify / InversifyJS

A powerful and lightweight inversion of control container for JavaScript & Node.js apps powered by TypeScript.
http://inversify.io/
MIT License
11.28k stars 716 forks source link

Any way to use it with create-react-app without ejecting? #1363

Open da1z opened 3 years ago

da1z commented 3 years ago

any way to use inversify with create-react-app without ejecting? especially @inject in parameters

PeteW commented 3 years ago

The short answer is there's no "one way" but I can share a pattern I've managed to scale out successfully.

Say you have some component props like:

export interface MyComponentProps extends RouteComponentProps<any> {
    id: string | null
    stateManager: MyStateManager
}

Then you have a component such as:

export class MyComponent extends React.Component<MyComponentProps, any> {
    constructor(props: Readonly<MyComponentProps>) {
        super(props)
    }
        render(): any: {
                return (...)
        }
}

Then you have some state management (this is just my pattern you may have some different use):

export interface MyStateManager {...}
import { injectable } from 'inversify'
@injectable()
export class SpecialStateManager implements MyStateManager {...}

So at this point you have a component and an interface and an injectable implementation. Now it is time to wire these together. One way to do this is to establish a container and configure dependencies.

I create a file container.ts with:

import { Container, interfaces } from 'inversify'
...
// the container is created once, witin this module and used throughout the application
export const container = new Container()
...
container.bind<MyStateManager>('MyStateManager').to(SpecialStateManager).inSingletonScope()

Inside App.tsx I would reference the pre-defined container like:


import { container } from '/path/to/container'
...
const App: React.FunctionComponent = () => {
    const myStateManager = container.get<MyStateManager>('MyStateManager')
...
    return (
        <Router basename={process.env.PUBLIC_URL}>
                    <Switch>
                        <Route exact path="/mypage/:id?" render={props => <MyComponent {...props} id={props.match.params.id}  stateManager={myStateManager} />} />
PeteW commented 3 years ago

Sorry I should have mentioned this. The benefit in this case is to allow swappable implementations of the state behind a react component, and swappable implementations of api clients, all driven by 2 REACT_APP_... configurations settings

Spektr commented 2 years ago

I think author asks about "how to run this" (typescript + cra+ inversify) without ejecting))) It's an old issue, now we can use "react-app-rewired" and "customize-cra" packages to achieve this goal. config.overrides.js must contain these lines:

module.exports = {
  webpack: override(
    /* for more info
     * @see babel-plugin-parameter-decorator README
     */
    addDecoratorsLegacy(),
    ...addBabelPlugins("babel-plugin-parameter-decorator"),
    )
}

also, there will be minor problems in typescript, and finally, I can't find a solution for using auto declaration in constructor and using not string keys:

// works fine
private service: SomeService;
constructor(@inject('SomeService') service: SomeService) {
  super(service);
  this.service = service;
}
// doesn't work
constructor(@inject('SomeService') private service: SomeService) {
}

try to find more info for using inversify in components in google, but I don't recommend doing so, because react already has lazy loading, functional components, and hooks system.

da1z commented 2 years ago

Yes, my question is about using it without decorators or setting cra to accept them. As I understand there are no clear answer yet