Skip to content

Latest commit

 

History

History
379 lines (236 loc) · 6.33 KB

api.md

File metadata and controls

379 lines (236 loc) · 6.33 KB

API


 

The App

Every application starts with a Mothership at the root to control its fleet.

 

<Mothership />

Sets up the environment, optionally using the passed app. Top-level features should be mounted as children of a Mothership.

props:

(Optional) app: FlyingSaucerApp

example:

<Mothership>
  <BrandChrome>
    <FeatureOne />
    <FeatureTwo path="/two" />
  </BrandChrome>
</Mothership>

 

createApp(config)

Create a new container for singletons used by Mothership.

arguments:

config: {

  (Optional) inject: {}

  (Optional) rematch?: RematchConfig

}

returns:

FlyingSaucerApp

example:

createApp({ inject: { api } })

 

Features

react-mothership is based around feature modules. The tree created by the react components at the root of your application describes the active features and how they are wired together - or in better words, what formation your fleet is flying in.

 

createFeature(config)(Base)

Decorate a React component at the root of a feature module. When mounted, it registers its dependencies with the context. Acts as a boundary for errors and suspense.

Want lazy loading? Use a React.lazy component!

arguments:

(Optional) config: {

  (Optional) name: string, // display name

  (Optional) placeholder: React.Element, // react element to show while suspended,

  (Optional) recovery: React.Element, // react element to show when suspense fails

  (Optional) models: [Rematch.Model], // rematch models to add to the store dynamically

  (Optional) views: [FlyingSaucerView], // views to provide in the parent scope

  (Optional) provides: {}, // concrete ambient dependencies

}

returns

FeatureComponent

example:

import Base from './Base'
import views from './views'
import models from './models'

const package = createFeature({
  views,
  models,
})

export default Base |> package

 

<FeatureComponent />

Behaves almost like a feature flag; mount this inside your mothership.

props:

(Optional) path: string

example:

import Feature from '@/features/Feature'
<Mothership>
  <Feature path="/somewhere" />
</Mothership>

 

RematchModel

Models encapsulate state logic.

When first mounted, a model will be registered with the store and built. Each of its factory functions will receive the inject property of the current Mothership.

props:

{

  name: string, // name to register globally

  (Optional) reducers: {
    [actionName]: (state, payload) => state
  }

  (Optional) selectors: (slice, createSelector, hasProps, inject) => {
    [selectorName]: models => (state, payload) => any
  }

  (Optional) effects: (dispatch, inject) => {
    [actionName]: async (state, payload) => any
  }

}

 

FlyingSaucerView

Views are declarative responses to app.history changes.

Each view will become a <Route /> in a <Switch />

  • component can be a React.lazy component and always takes precedence
  • render, effect, and redirect can be combined for a side-effect in response to routing.

props:

{

  name: string

  path: string

  (Optional) resolve: (params: {}) => string // map params to formatted path

  (Optional) component: React.Component

  (Optional) render: (props: {}) => React.Component

  (Optional) effect: (dispatch: RematchDispatch) => any,

  (Optional) redirect: string | { to, ...params }

}

 

Enhanced Routing

Every routing primitive from react-router is available from react-mothership. Any components that take a to prop can also resolve it from the current scope's views.

example:

import { Switch, Route, Redirect, Link, NavLink } from '@@'
function Nope({ match }) {
  return <Redirect view="nope" params={match} />
}

props:

view: string
params: any

todo:

You may notice we export components from react-router-dom; this might be revisited to use some form of DI

 

Context Bindings

 

useApp()

Hook to retrieve the mothership's app configuration.

returns:

FlyingSaucerApp

example:

const app = useApp()

 

useProvided(...paths)

Hook to retrieve the current scopes's ambient dependencies.

arguments:

paths: string[]

returns:

any[]

throws:

NotProvidedInScopeError

example:

const [Layout, appName] = useProvided('Layout', 'config.app.name')

 

Redux Bindings

 

sconnect(mapSelect, mapDispatch)(Base)

alias: $$

react-redux's connect function that retrieves the Mothership's selectors instead of directly mapping state.

arguments:

mapSelect: RematchSelect => {}
mapDispatch: RematchDispatch => {}

example:

const enhance = $$(
  select => ({
    todos: select.todos.list,
  }),
  dispatch => ({
    addTodo: dispatch.todos.addTodo,
  })
)

 

withDispatch(mapDispatch)(Base)

alias: _$

connects only dispatchers. $$ but skips the first argument.

arguments:

mapDispatch: RematchDispatch => {}

example:

const enhance = _$(dispatch => ({
  addTodo: dispatch.todos.addTodo,
}))

 

useAppEffect(effectWithDispatch, watch)

Hook to run dispatchers as a side-effect when any value in watch changes.

arguments:

effectWithDispatch: RematchDispatch => any
watch: any[]

example:

useAppEffect(dispatch => dispatch.storage.retrieve(props.id), [props.id])

 

useAppSelector(mapSelect, payload, deps)

Hook to pick a selector from the current store and run it. The ref to mapSelect will change when deps changes. Mapped selectors will be passed payload as ownProps.

arguments:

mapSelect: RematchSelect => any
payload: any
deps: [any]

example:

const todos = useAppSelector(it.todos.list)