# Managers
Managers can be generated by running `hatch manager <ModuleName>`

Managers tie the UI to business logic defined via [Service](../services) modules. Managers do this by 
defining two types of methods: 
[location change handlers](#location-change-handlers) and 
[client load handlers](#client-load-handlers). Either of these methods types can be defined as `async` methods, or 
[as Sagas](#manager-methods-as-sagas).


#### Location change handlers
Methods decorated with `@onLocationChange()`, which run whenever a UI route is loaded. These methods run both on the
server and on the client. The server runs them during server-side rendering for the route that has been requested
by the browser. If server-side rendering is enabled, by default the client does not run onLocationChange handlers on the 
initial client load, as it would have been run during the initial server-side render. The client does run the location 
change handler on all client-side navigation events after (but not including) the initial load. Furthermore, if the 
runOnClientLoad argument is passed to onLocationChange (e.g. @onLocationChange({runOnClientLoad = true}), the handler is 
run on the client during the initial client load. This can be useful for scenarios when you want the client to fetch 
data after an initial "empty" load from the server. Often, this will be paired with a check for `!isServer` to ensure that 
the client-side loading of data is not run server-side.

The decorator applied to these methods can optionally constrain which routes will cause the method to run, by passing
in either a string representing a path, or an object which conforms to
[React Router's Route Props](https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/Route.md#route-props)
type. Note only the following are fields of this interface are really relevant to this use case: 
* [`path`](https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/Route.md#path-string--string)
* [`exact`](https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/Route.md#exact-bool)
* [`sensitive`](https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/Route.md#sensitive-bool)
* [`strict`](https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/Route.md#strict-bool)

When passing the parameter as an object, the following optional field is allowed:
* runOnClientLoad - boolean used to specify if it should be run on the client's first load in addition to the server after 
initial rendering

As an example, here's a method that is only called when the `/hi` route is loaded:

```typescript
@onLocationChange({path: '/hi'})
public async prepGreeting() {
  this.logger.info('Hello, world!');
}
```
Note that this could also just use the string '/hi' as a parameter:
```typescript
@onLocationChange('/hi')
public async prepGreeting() {
  this.logger.info('Hello, world!');
}
```
Routes paths can also contain route parameters, e.g. extracting a user ID from the route:
```typescript
@onLocationChange('/user/:id')
public async prepRoute(context: LocationChangeContext<{id: string}>) {
  this.logger.info('Loaded user: ' + context.pathMatch.params.id);
}
```

As noted, a method is by default only ran by the server during the initial server-side rendering. To have the method 
additionally ran by client on first load:
```typescript
@onLocationChange({path: '/hi', runOnClientLoad: true})
public prepRunOnClientLoad({isServer}: LocationChangeContext) {
  if (!isServer) {
    this.logger.info('Only logged by client');
  } else {
    this.logger.info('Only logged by server');
  }
}
```

As shown above, these methods can take in a `LocationChangeContext` object, which contains information about the 
route change that has been applied and whether to run additionally on client's first load.

However, making use of dependency injection, these methods can also take in any class registered in the dependency 
injection container. The container used to resolve the dependency will be injected with the fields of
`LocationChangeContext`, meaning complex objects depending on these fields can be constructed.

For example, imagine we had a class `UserContext` which only needs an ID to be constructed:
```typescript
@injectable
class UserContext {
  public id: string;

  constructor(@inject('pathMatch') pathMatch: match<{id: string}>) {
    this.id = pathMatch.params.id;
  }
}
```

Our user ID location change handler above could simply be written as this:

```typescript
@onLocationChange('/user/:id')
public async prepRoute(context: UserContext) {
  this.logger.info('Loaded user: ' + context.id);
}
```
As you might imagine, much more complex object trees could potentially be derived from a container injected with a
`LocationChangeContext` object.

#### Client load handlers
Methods decorated with `@onClientLoad()`, which run when the client loads. Common use cases for these are for setting
up long-running effects (e.g. refresh timers, or `takeEvery` saga effects).

The root parameter type for these methods is `ClientLoadContext`. However, like with `@onLocationChange` methods, 
these methods can build up more complex parameter types, making use of dependency injection. The container used to
resolve the dependency will be injected with the fields of `ClientLoadContext`.

#### Manager methods as Sagas
All of the examples above show `async` methods. For simple use cases, this might be sufficient. However, for more 
complex cases, these methods can also be generators which yield 
[Redux Saga Effects](https://redux-saga.js.org/docs/basics/DeclarativeEffects.html). For example:
   ```typescript
   @onLocationChange({path: '/hi'})
   public *prepGreeting() {
     yield put(actions.showGreeting());
   }
   ```

   ```typescript
   @onClientLoad()
   public *registerButtonHandler() {
     yield takeLatest(actions.buttonClick, this.handleButtonClick.bind(this));
   }
   ```
