# Helpers
Strapi provides helpers so you don't have to develop again and again the same generic functions.
## Auth
`auth.js` lets you get, set and delete data in either the browser's `localStorage` or `sessionStorage`.
### Methods
| Name | Description |
| ---- | ----------- |
| clear(key) | Remove the data in either `localStorage` or `sessionStorage` |
| clearAppStorage() | Remove all data from both storage |
| clearToken() | Remove the user's `jwt Token` in the appropriate browser's storage |
| clearUserInfo() | Remove the user's info from storage |
| get(key) | Get the item in the browser's storage |
| getToken() | Get the user's `jwtToken` |
| getUserInfo() | Get the user's infos |
| set(value, key, isLocalStorage) | Set an item in the `sessionStorage`. If `true` is passed as the 3rd parameter it sets the value in the `localStorage` |
| setToken(value, isLocalStorage) | Set the user's `jwtToken` in the `sessionStorage`. If `true` is passed as the 2nd parameter it sets the value in the `localStorage` |
| setUserInfo(value, isLocalStorage) | Set the user's info in the `sessionStorage`. If `true` is passed as the 2nd parameter it sets the value in the `localStorage` |
```js
import auth from 'utils/auth';
// ...
//
auth.setToken('12345', true); // This will set 1234 in the browser's localStorage associated with the key: jwtToken
```
## Colors
This function allows to darken a color.
### Usage
```js
import { darken } from 'utils/colors';
const linkColor = darken('#f5f5f5', 1.5); // Will darken #F5F5F5 by 1.5% which gives #f2f2f2.
```
## Get URL Query Parameters
The helpers allows to retrieve the query parameters in the URL.
### Example
```js
import getQueryParameters from 'utils/getQueryParameters';
const URL = '/create?source=users-permissions';
const source = getQueryParameters(URL, 'source');
console.log(source); // users-permissions
```
## Request helper
A request helper is available to handle all requests inside a plugin.
It takes three arguments:
- `requestUrl`: The url we want to fetch.
- `options`: Please refer to this [documentation](https://github.com/github/fetch).
- `true`: This third argument is optional. If true is passed the response will be sent only if the server has restarted check out the [example](#example-with-server-autoreload-watcher).
### Usage
**Path -** `/plugins/my-plugin/admin/src/containers/**/sagas.js`.
```js
import { call, fork, put, takeLatest } from 'redux-saga/effects';
// Our request helper
import request from 'utils/request';
import { dataFetchSucceeded, dataFetchError } from './actions';
import { DATA_FETCH } from './constants';
export function* fetchData(action) {
  try {
    const opts = {
      method: 'GET',
    };
    const requestUrl = `/my-plugin/${action.endPoint}`;
    const data = yield call(request, requestUrl, opts);
    yield put(dataFetchSucceeded(data));
  } catch(error) {
    yield put(dataFetchError(error))
  }
}
// Individual exports for testing
function* defaultSaga() {
  yield fork(takeLatest, DATA_FETCH, fetchData);
}
export default defaultSaga;
```
### Simple example
Let's say that we have a container that fetches Content Type configurations depending on URL change.
#### Routing declaration:
Here we want to create a route `/content-type/:contentTypeName` for the `ContentTypePage` container.
**Path —** `./plugins/my-plugin/admin/src/container/App/index.js`.
```js
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators, compose } from 'redux';
import { createStructuredSelector } from 'reselect';
import { Switch, Route, withRouter } from 'react-router-dom';
import PropTypes from 'prop-types';
import { pluginId } from 'app';
import ContentTypePage from 'containers/ContentTypePage';
import styles from './styles.scss';
class App extends React.Component {
  render() {
    return (
      
        
          
        
      
    );
  }
}
App.contextTypes = {
  router: PropTypes.object.isRequired,
};
export function mapDispatchToProps(dispatch) {
  return bindActionCreators(
    {},
    dispatch
  );
}
const mapStateToProps = createStructuredSelector({});
const withConnect = connect(mapStateToProps, mapDispatchToProps);
export default compose(
  withConnect,
)(App);
```
***
#### Constants declaration:
Let's declare the needed constants to handle fetching data:
**Path —** `./plugins/my-plugin/admin/src/containers/ContentTypePage/constants.js`.
```js
export const DATA_FETCH = 'myPlugin/ContentTypePage/DATA_FETCH';
export const DATA_FETCH_ERROR = 'myPlugin/ContentTypePage/DATA_FETCH_ERROR';
export const DATA_FETCH_SUCCEEDED = 'myPlugin/ContentTypePage/DATA_FETCH_SUCCEEDED';
```
***
#### Actions declaration:
Let's declare our actions.
**Path —** `./plugins/my-plugin/admin/src/containers/ContentTypePage/actions.js`.
```js
import {
  DATA_FETCH,
  DATA_FETCH_ERROR,
  DATA_FETCH_SUCCEEDED,
} from './constants';
export function dataFetch(contentTypeName) {
  return {
    type: DATA_FETCH,
    contentTypeName,
  };
}
export function dataFetchError(errorMessage) {
  return {
    type: DATA_FETCH_ERROR,
    errorMessage,
  };
}
export function dataFetchSucceeded(data) {
  // data will look like { data: { name: 'User', description: 'Some description' } }
  return {
    type: DATA_FETCH_SUCCEEDED,
    data,
  };
}
```
***
#### Reducer setup:
Please refer to the [Immutable documentation](https://facebook.github.io/immutable-js/docs/#/) for informations about data structure.
**Path —** `./plugins/my-plugin/admin/src/containers/ContentTypePage/reducer.js`.
```js
import { fromJS, Map } from 'immutable';
import {
  DATA_FETCH,
  DATA_FETCH_ERROR,
  DATA_FETCH_SUCCEEDED
} from './constants';
const initialState = fromJS({
  contentTypeName,
  error: false,
  errorMessage: '',
  data: Map({}),
});
function contentTypePageReducer(state = initialState, action) {
  switch (action.type) {
    case DATA_FETCH:
      return state.set('contentTypeName', action.contentTypeName);
    case DATA_FETCH_ERROR:
      return state
        .set('error', true)
        .set('errorMessage', action.errorMessage);
    case DATA_FETCH_SUCCEEDED:
      return state
        .set('error', false)
        .set('data', Map(action.data.data));
    default:
      return state;
  }
}
export default contentTypePageReducer;
```
***
#### Selectors setup:
**Path —** `./plugins/my-plugin/admin/src/containers/ContentTypePage/selectors.js`.
```js
import { createSelector } from 'reselect';
/**
 * Direct selector to the contentTypePage state domain
 */
const selectContentTypePageDomain = () => state => state.get('contentTypePage');
/**
 * Other specific selectors
 */
/**
 * Default selector used by ContentTypePage
 */
const selectContentTypePage = () => createSelector(
  selectContentTypePageDomain(),
  (substate) => substate.toJS()
);
const makeSelectContentTypeName = () => createSelector(
  selectContentTypePageDomain(),
  (substate) => substate.get('contentTypeName');
)
export default selectContentTypePage;
export { makeSelectContentTypeName, selectContentTypePageDomain };
```
***
#### Handling route change:
**Path —** `./plugins/my-plugin/admin/src/containers/ContentTypePage/index.js`.
```js
import React from 'react';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { bindActionCreators, compose } from 'redux';
import { NavLink } from 'react-router-dom';
import PropTypes from 'prop-types';
import { map } from 'lodash';
// Utils to create the container's store
import injectSaga from 'utils/injectSaga';
import injectReducer from 'utils/injectReducer';
import { dataFetch } from './actions';
import { selectContentTypePage } from './selectors';
import saga from './sagas';
import reducer from './reducer';
import styles from './styles.scss';
export class ContentTypePage extends React.Component { // eslint-disable-line react/prefer-stateless-function
  constructor(props) {
    super(props);
    this.links = [
      {
        to: 'plugin/my-plugin/content-type/product',
        info: 'Product',
      },
      {
        to: 'plugin/my-plugin/content-type/user',
        info: 'User',
      },
    ];
  }
  componentDidMount() {
    this.props.dataFetch(this.props.match.params.contentTypeName);
  }
  componentWillReceiveProps(nextProps) {
    if (nextProps.match.params.contentTypeName !== this.props.match.params.contentTypeName) {
      this.props.dataFetch(nextProps.match.params.contentTypeName);
    }
  }
  render() {
    return (
      
        
          
            {map(this.links, (link, key) => (
              - 
                {link.info}
              ))}
 
        
          {this.props.data.name}
          {this.props.data.description}
         
       
    );
  }
}
const mapStateToProps = selectContentTypePage();
function mapDispatchToProps(dispatch) {
  return bindActionCreators(
    {
      dataFetch,
    },
    dispatch,
  );
}
ContentTypePage.propTypes = {
  data: PropTypes.object.isRequired,
  dataFetch: PropTypes.func.isRequired,
  match: PropTypes.object.isRequired,
};
const withConnect = connect(mapStateToProps, mapDispatchToProps);
const withSaga = injectSaga({ key: 'contentTypePage', saga });
const withReducer = injectReducer({ key: 'contentTypePage', reducer });
export default compose(
  withReducer,
  withSaga,
  withConnect,
)(ContentTypePage);
```
***
#### Fetching data:
The `sagas.js` file is in charge of fetching data.
**Path —** `./plugins/my-plugin/admin/src/containers/ContentTypePage/sagas.js`.
```js
import { LOCATION_CHANGE } from 'react-router-redux';
import { takeLatest, call, take, put, fork, cancel, select } from 'redux-saga/effects';
import request from 'utils/request';
import {
  dataFetchError,
  dataFetchSucceeded,
} from './actions';
import { DATA_FETCH } from './constants';
import { makeSelectContentTypeName } from './selectors';
export function* fetchData() {
  try {
    const opts = { method: 'GET' };
    // To make a POST request { method: 'POST', body: {Object} }
    const endPoint = yield select(makeSelectContentTypeName());
    const requestUrl = `my-plugin/**/${endPoint}`;
    // Fetching data with our request helper
    const data = yield call(request, requestUrl, opts);
    yield put(dataFetchSucceeded(data));
  } catch(error) {
    yield put(dataFetchError(error.message));
  }
}
function* defaultSaga() {
  const loadDataWatcher = yield fork(takeLatest, DATA_FETCH, fetchData);
  yield take(LOCATION_CHANGE);
  yield cancel(loadDataWatcher);
}
export default defaultSaga;
```
***
### Example with server autoReload watcher
Let's say that you want to develop a plugin that needs server restart on file change (like the settings-manager plugin) and you want to be aware of that to display some stuff..., you just have to send a third argument: `true` to our request helper and it will ping a dedicated route and send the response when the server has restarted.
**Path —** `./plugins/my-plugin/admin/src/containers/**/sagas.js`.
```js
import { takeLatest, call, take, put, fork, cancel, select } from 'redux-saga/effects';
import request from 'utils/request';
import {
  submitSucceeded,
  submitError,
} from './actions';
import { SUBMIT } from './constants';
// Other useful imports like selectors...
// ...
export function* postData() {
  try {
    const body = { data: 'someData' };
    const opts = { method: 'POST', body };
    const requestUrl = `**yourUrl**`;
    const response = yield call(request, requestUrl, opts, true);
    if (response.ok) {
      yield put(submitSucceeded());      
    } else {
      yield put(submitError('An error occurred'));
    }
  } catch(error) {
    yield put(submitError(error.message));
  }
}
function* defaultSaga() {
  yield fork(takeLatest, SUBMIT, postData);
  // ...
}
export default defaultSaga;
```