Creating Higher-Order Component
Learn to refactor our code from the previous chapter to make a higher-order component in this lesson.
We'll cover the following
Creating reusable higher-order component
At the bottom of this lesson, we have the test-driven-carousel
project from the previous chapter, take a look at the Carousel
component. In addition to rendering a somewhat complex DOM tree, it also has one piece of state and two event handlers that manipulate that state. Let’s try building an HOC that encapsulates that logic.
-
Well-implemented HOCs tend to be highly reusable, and this one will be no exception. It’ll manage the state for any component with an “index” prop, meaning a number that can go from 0 up to some limit. Call it
HasIndex
. Start with a minimal dummy implementation that you can run tests against:// src/HasIndex.js import React from 'react'; export default (Component, indexPropName) => class ComponentWithIndex extends React.PureComponent { static displayName = `HasIndex(${Component.displayName || Component.name})`; render() { return <Component {...this.props} />; } };
-
To replace the
slideIndex
logic inCarousel
, we needHasIndex
to provide three props to the wrapped component: the index itself (with the givenindexPropName
), an increment function, and a decrement function. To support wrap-around, the increment and decrement functions should accept an upper bound argument. Write a test suite with those requirements:// src/tests/HasIndex.test.js import React from 'react'; import { shallow } from 'enzyme'; import HasIndex from '../HasIndex'; describe('HasIndex()', () => { const MockComponent = () => null; MockComponent.displayName = 'MockComponent'; const MockComponentWithIndex = HasIndex(MockComponent, 'index'); it('has the expected displayName', () => { expect(MockComponentWithIndex.displayName).toBe( 'HasIndex(MockComponent)' ); }); let wrapper; beforeEach(() => { wrapper = shallow(<MockComponentWithIndex />); }); it('has an initial `index` state of 0', () => { expect(wrapper.state('index')).toBe(0); }); it('passes the `index` state down as the `index` prop', () => { expect(wrapper.prop('index')).toBe(0); wrapper.setState({ index: 1 }); expect(wrapper.prop('index')).toBe(1); }); it('has an `index` state of 2 on decrement from 3', () => { wrapper.setState({ index: 3 }); wrapper.prop('indexDecrement')(); expect(wrapper.state('index')).toBe(2); }); it('has an `index` state of 1 on increment', () => { wrapper.prop('indexIncrement')(); expect(wrapper.state('index')).toBe(1); }); it('has the max `index` state on decrement from 0', () => { wrapper.setState({ index: 0 }); wrapper.prop('indexDecrement')(3); expect(wrapper.state('index')).toBe(2); }); it('has the min `index` state on increment from the max', () => { wrapper.setState({ index: 2 }); wrapper.prop('indexIncrement')(3); expect(wrapper.state('index')).toBe(0); }); });
-
Then try modifying the implementation to meet those requirements. You should end up with something like this:
Get hands-on with 1400+ tech skills courses.