How to make good React components
Architecture
Folder toto
- toto.component.js
- toto.container.js
- toto.style.(css|js)
- toto.test.js
- index.js
Component File
- Must be under 200 lines long
- No function declared in the render
- Type :
- Component
- PureComponent
- Functional
Component File
import React from 'react';
import PropTypes from 'prop-types';
import styles from './menu-link.style.css';
export default class MenuLink extends React.PureComponent {
render() {
return (
<div className={styles.menuLink} onClick={this.props.next}>
<span className={styles.label}>
{this.props.label}
</span>
</div>
);
}
}
MenuLink.defaultProps = {
label: '-',
next: () => {},
};
MenuLink.propTypes = {
alertStatus: PropTypes.numb.isRequired,
next: PropTypes.func.isRequired,
label: PropTypes.string.isRequired,
};
Style File
- Belongs to the component
- Agnostic
Container File
- No render function
- Provides data and behavior
to the presentational component
Container File
import { connect } from 'react-redux';
import MenuLink from './menu-link.component';
import { next } from '../actions/navigate';
function mapStateToProps(state) {
return {
alertStatus: state.alertStatus
};
}
function mapDispatchToProps() {
return {
next: () => dispatch(next()),
};
}
export default connect(
mapStateToProps,
mapDispatchToProps,
)(MenuLink);
Test File
- Contains Unit tests (functions)
- Contains Snapshot tests
Test file
import React from 'react';
import { shallow } from 'enzyme';
import renderer from 'react-test-renderer';
import MenuLink from './menu-link.component';
describe('[Unit] MenuLink', () => {
it('Should call the "next" function', () => {
const nextMock = jest.fn();
container.setProps({ alertStatus: 1, label: 'Link !', next: nextMock });
container.simulate('click');
expect(nextMock.mock.calls.length).toEqual(1);
});
});
describe('[Snapshot] MenuLink', () => {
const data = [
{
props: { alertStatus: 1, label: 'A sweet button', next: () => {} },
label: 'default',
},
];
data.forEach(({ props, label }) => {
const container = renderer.create(<MenuLink {...props} />);
it(`should match snapshot : ${label}`, () => {
expect(container.toJSON()).toMatchSnapshot();
});
});
});
Extract Containers from coverage
"jest": {
"rootDir": ".",
"roots": [
"<rootDir>/client/source",
],
"moduleNameMapper": {
"^.+\\.(css|less)$": "<rootDir>/client/source/CSSStub.js",
"^.+\\.(jpg|png)$": "<rootDir>/client/source/imgStub.js",
"^.+\\.(svg)$": "<rootDir>/client/source/svgStub.js"
},
"collectCoverage": true,
"coverageDirectory": "<rootDir>/coverage",
"collectCoverageFrom": [
"**/*.{js,jsx}",
"!**/*.container.{js,jsx}"
],
"coveragePathIgnorePatterns": [
"<rootDir>/client/source/actions/",
"<rootDir>/node_modules/"
]
},
Refactoring
- Create the folder and files
- Write or rework tests
- Extract connections
- Extract styles
React Components
By Gabriel Andrin
React Components
How to make good react components
- 684