# Using Repertoire

Repertoire works together with **React** and **Redux**, so make sure you have the following packages installed in your application:

* ​[react](https://www.npmjs.com/package/react)​
* ​[redux](https://www.npmjs.com/package/redux)​
* ​[react-redux](https://www.npmjs.com/package/react-redux)​

## Creating Controllers

The main purpose of a controller is to encapsulate actions which will eventually update the Redux store. The *constructor* is where we define the store properties that are to be mapped to the React component.

A controller will receive one React component as argument.

```javascript
import {BaseController} from 'repertoire'

export default class AdminController extends BaseController {
  constructor(component) {
    super(component);
    
    this.state = {
      // ...
    };
    
    this.connect();
  }
}
```

### Adding Actions

The essence of the controller are the actions. These actions, along with the state properties, are mapped automatically to the React component. You can also map them yourself to another component which is not linked to the controller, by using the [connect()](https://github.com/reduxjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options) utility from [react-redux](https://github.com/reduxjs/react-redux) package. More on that later.

Defining a new action is as simple as adding a new method to the controller. Every action has an implicit *reducer* which will updated the Redux store.

{% hint style="warning" %}
The action needs to return a **Promise**. The result of the promise will be merged with the existing state of the Redux store.
{% endhint %}

```javascript
import {BaseController} from 'repertoire'

export default class AdminController extends BaseController {
  fetchUsers () {
    return AdminApi.getUsers().then(result => {
    
      // the result of this Promise will update the Redux store,
      // by calling an implicit reducer internally
      return {
        users: result
      };
    });
  }
  
  constructor(component) {
    super(component);
    
    this.state = {
      // ...
    };
    
    this.connect();
  }
}
```

Repertoire uses [**redux-saga**](https://redux-saga.js.org/) internally as the Redux middleware, but you'll most likely not have to care about it.

### Mapping Store State to React

In order to map the Redux store state to React props, you will need to define the controller`state`. This will automatically be made available to the React component as properties, along with the controller actions.

In the previous step there is an action which eventually will resolve with the object `{users: result}` and this will update the store.&#x20;

The following will result in a React prop containing the `users` store value.

```javascript
import {BaseController} from 'repertoire'

export default class AdminController extends BaseController {
  fetchUsers () {
    return AdminApi.getUsers().then(result => {
    
      // the result of this Promise will update the Redux store,
      // by calling an implicit reducer internally
      return {
        users: result
      };
    });
  }
   
  constructor(component) {
    super(component);
    
    this.state = {
      users(store) {
        return store.users || [];
      }
    };
    
    this.connect();
  }
}
```

### Controller Namespace

If you would like to use a specific namespace on the Redux store, most likely because you want two or more controllers to operate on the same store segment, you can use the `stateNamespace` property.

```javascript
import {BaseController} from 'repertoire'

export default class AdminController extends BaseController {
  // the section of the redux store which this controller will operate on
  get stateNamespace() {
    return 'admin';
  }

  setCurrentUser(currentUser) {
    return {
      currentUser
    }
  }
    
  fetchUsers () {
    // ...
  }
  
  constructor(component) {
    super(component);
    
    // ...
  }
}
```

## Connecting Store Updates Manually

Sooner or later you will probably need to manually connect the Redux store state or dispatch actions to a component which is not directly linked to a controller, by using the [connect()](https://github.com/reduxjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options) utility.

To do so, you can reference existing actions using `dispatchActions` and bind them using the `bindActionCreators` utility from Redux.

```jsx
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {dispatchActions} from 'repertoire'

class User extends Component { 
  componentWillMount() {
    this.props.setCurrentUser(this.state.selectedUser);
  }
  
  render() {
    const {users} = this.props;
    
    //...
  } 
}

const mapStateToProps = state => ({
  users: state.admin.users
});

const mapDispatchToProps = dispatch => bindActionCreators({
  setCurrentUser: dispatchActions.setCurrentUser
}, dispatch);

export default connect(mapStateToProps, mapDispatchToProps)(User);
  
```

## �Error Handling

Repertoire is handling errors thrown from Controller actions automatically and adding the last error on the Redux store, as the `lastThrownError` property. This is available automatically.

Whenever the promise from a Controller action throws or rejects, the store will be updated with the error object automatically.&#x20;

The error object will also contain an `initiator` property, which will reflect the action which has caused the error.

{% code title="controller.js" %}

```javascript
import {BaseController} from 'repertoire'

export default class AdminController extends BaseController {
  // the section of the redux store which this controller will operate on
  get stateNamespace() {
    return 'admin';
  }
   
  fetchUsers () {
    // simulating an exception
    return new Promise((resolve, reject) => {
      reject(new Error('Not Found'));
    });
  }
  
  constructor(component) {
    super(component);
    
    this.state = {
      users(store) {
        return store.users || [];
      }
    };
    
    this.connect();
  }
}
```

{% endcode %}

{% code title="component.js" %}

```jsx
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import Controller from './controller.js';

class Admin extends Component {
  static propTypes = {
    users: PropTypes.array.isRequired,
    fetchUsers: PropTypes.func.isRequired
  };

  constructor(props) {
    super(props);
    // ...
  }

  componentWillMount() {
    this.props.fetchUsers();
  }

  render() {
    const {users, lastThrownError} = this.props;
    
    // lastThrownError.initiator === 'fetchUsers'
    console.error(lastThrownError);
    
    return users.length > 0 ? <div>
      // ...
    </div> : null;
  }
}

export default new Controller(Admin);
```

{% endcode %}

### Catching Errors from Actions

You can also catch errors from actions yourself and then export the error object. If you export the error using the `lastError` property name, it will also contain an `initiator` property.

{% code title="controller.js" %}

```javascript
import {BaseController} from 'repertoire'

export default class AdminController extends BaseController {
  // the section of the redux store which this controller will operate on
  get stateNamespace() {
    return 'admin';
  }
   
  fetchUsers () {
    // simulating an exception
    return new Promise((resolve, reject) => {
      reject(new Error('Not Found'));
    }).catch(err => ({
      users: [],
      lastError: err
    }));
  }
  
  constructor(component) {
    super(component);
    
    this.state = {
      users(store) {
        return store.users || [];
      }
    };
    
    this.connect();
  }
}
```

{% endcode %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://repertoire.gitbook.io/home/using-repertoire.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
