Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: support multiple services with one provider #14

Open
adamdickinson opened this issue May 28, 2021 · 0 comments
Open

Feature request: support multiple services with one provider #14

adamdickinson opened this issue May 28, 2021 · 0 comments

Comments

@adamdickinson
Copy link
Owner

As mentioned in the README, having many services offered by an app gets a bit intense. Because of how context works, it means we'd be looking at a lot of top-level nesting.

There is another possible option however - shared context.

Implementation Goal

The best APIs are designed for those who use them, not those who build them, so it's worth looking at how we might like to apply a single-provider service structure:

import { Services } from '@adamdickinson/react-service'

import { AuthService } from './services/auth'
import { NoticeService } from './services/notice'
import { ReportService } from './services/report'

const App = () => (
  <Services services={[AuthService, NoticeService, ReportService]}>
    ...
  </Services>
)
import { useAuth } from './services/auth'

const SubComponent = () => {
  const { user } = useAuth()
  return <h1>{user?.name ?? 'Not logged in'}</h1>
}

And if we wanted to, we should be able to expose services at various points in our app:

const App = () => (
  <>
    <Services auth={AuthService}>
      <Services report={ReportService}>
        ...
      </Services>
    </Services>

    <Services notice={NoticeService}>
      ...
    </Services>
  </>
)

The smarter way to handle this would be as follows, but the above illustrates being able to expose many services with only a few providers well:

const App = () => (
  <>
    <AuthService>
      <ReportService>
        ...
      </ReportService>
    </AuthService>

    <NoticeService>
      ...
    </NoticeService>
  </>
)

How do we accomplish this?

A single context seems to be the way...

const ServiceContext = React.createContext()

interface ServicesProps {
  services: Service[]
}

const Services = ({ children, ...services }) => {
  const existingApis = useContext(ServiceContext)
  const apis = { 

    // Carry forward higher-level services
    ...existingApis

    // Add newly defined services
    ...Object.keys(services).reduce((newApis, name) => {
      apis[name] = services[name].useApi()
    }, newApis)

  }

  return (
    <ServiceContext.Provider value={apis}>
      {children}
    </ServiceContext.Provider>
  )
}

Benefits

The simple benefit is that we can now throw in a heap of services without a nesting nightmare, but we could do that without many architectural changes, so why does the above proposal attempt to wrap everything in a single context? Because it allows us to get services talking to each other. Let's say you have a Data service that uses Auth info, and an Auth service that will make use of unauthorised processes in the Data service - this structure will allow us to do this.

More thoughts on the way on this one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant