Category Archives: ES6

React + Recompose: Calling Multiple HOC Wrappers

PROBLEM

Sometimes, wrapping a React component with multiple High Order Components (HOC) can get rather unwieldy and unreadable.

For example:-

import React from 'react';
import { withRouter } from 'react-router-dom';
import { withStyles } from 'material-ui/styles';
import withWidth from 'material-ui/utils/withWidth';

class MyComponent extends React.PureComponent {
	// ...
}

export default withRouter(withStyles(styles)(withWidth()(MyComponent)));

SOLUTION

To fix this, we can leverage recompose library.

Now, we can rewrite the above example like this:-

import React from 'react';
import { withRouter } from 'react-router-dom';
import { withStyles } from 'material-ui/styles';
import withWidth from 'material-ui/utils/withWidth';
import compose from 'recompose/compose';

class MyComponent extends React.PureComponent {
	// ...
}

export default compose(
  withRouter,
  withStyles(styles),
  withWidth(),
)(MyComponent);

Keep in mind, the HOC order defined in compose(..) is important.

Advertisements

ES6 + Mocha + Sinon: Mocking Imported Dependency

PROBLEM

Let’s assume we have the following 2 files:-

apis.js

import fetch from 'isomorphic-fetch';

export const logout = () => (
  fetch('/logout')
    .then(resp => resp.json())
    .catch(err => err)
);

service.js

import { logout } from './apis';

export const kickUserOut = activeSession => (
  activeSession ? logout() : undefined
);

Let’s assume we want to test the logic in service.js without using nock to mock the HTTP call in apis.js.

While proxyquireify allows us to mock out the apis.js dependency in service.js, sometimes it is a little more complicated than needed.

SOLUTION

A simpler approach is to use sinon to stub out logout() defined in apis.js.

service-spec.js

import { beforeEach, afterEach, describe, it } from 'mocha';
import { expect } from 'chai';
import sinon from 'sinon';
import { kickUserOut } from './service';

// import everything as an object
import * as apis from './apis';

describe('service => kickUserOut', () => {
  let logoutStub;

  // before running each test, stub out `logout()`
  beforeEach(() => {
    logoutStub = sinon.stub(apis, 'logout').returns('success');
  });

  // after running each test, restore to the original method to
  // prevent "TypeError: Attempted to wrap logout which is already wrapped"
  // error when executing subsequent specs.
  afterEach(() => {
    apis.logout.restore();
  });

  it('given active session, should invoke logout API', () => {
    expect(kickUserOut(true)).to.deep.equal('success');
    expect(logoutStub.calledOnce).to.equal(true);
  });

  it('given expired session, should not invoke logout API', () => {
    expect(kickUserOut(false)).to.equal(undefined);
    expect(logoutStub.calledOnce).to.equal(false);
  });
});