Components in our application is broken into 4 major types: elements, blocks, decorators, and containers.
Elements are the smallest of the bunch and are considered the building blocks of our application. The goal being that they make up the base of our component hierarchy. It's easier and scalable if our component hierarchy is built upon these reusable, highly maluable pieces. If we our baseline elements are unoppinionated, pure, stateless, functional components then it makes extending upwards a lot easier.
Ultimately are components that just return jsx
.
const TextInput = ({ value, handleChange, name }) => (
<input
type="text"
value={value}
onChange={handleChange}
name={name}
/>
)
Whats nice about these components is that they will always be that particular input regardless of where you put them or place them. They also have the added beneifit of cleaning up our API making things a lot easier to read in the long run.
Blocks like the name suggest are UI componets comprised of smaller element
compents. At their core they should have no logic. Like the elements
they should simply return JSX
. Any logic, mapping, etc should be moved out of this component. It can receive props but it's main job is to simply be a composition of elements
import React from 'react';
import { TextInput, Button } from '../../elements';
const VerificationForm = ({ handleChange, handleSubmit, verification }) => {
return (
<form onSubmit={handleSubmit}>
<TextInput value={verification} handleChange={handleChange} name="verification" />
<Button value="Verify" />
</form>
);
};
import { VerificationForm } from '../../blocks';
class BankVerification extends Component {
...
render () {
<VerificationForm
handleChange={this.handleChange}
handleSumbit={this.handleSubmit}
verification={this.state.verification}
/>
}
}
Notice that this block is essentially comprised of the small building blocks. The intent being if something were to change on the element
level that change is then effected across the board instead of going through component by component. The point of the abstraction is for readibility and to seperate concerns.
Decorators can be any type of component (functional, stateful, class based, etc). The main thing that set's these components apart are the fact that they contain some sort of logic. They are either keeping track of state, itterating over a collection, or rendering other blocks
. It should rarely render elements
but we understand that there are times when that is a better solution. The relationship between decorator
and block
should be one that encourages a slim render function.
import React, { Component } from 'react';
import { VerificationForm } from '../../blocks';
class BankVerification extends Component {
state = {
verification: ''
};
handleSubmit = event => {
event.preventDefault();
const { handleVerification, bankAccountId } = this.props;
const { verification } = this.state.verification
handleVerification({ verification, bankAccountId });
};
handleChange = ({ target: { value: verification } }) => this.setState({ verification });
render() {
return (
<section style={this.props.style}>
<VerificationForm
handleChange={this.handleChange}
handleSubmit={this.handleSubmit}
{...this.state}
/>
</section>
);
}
}
export default BankVerification;
Containers are components that are connected to Apollo
. They should handle mutations, queries, and errors. Their primary purpose is to feed the decorators
with the necessary props to function. They can be stateful
components but should lean towards being a provider of props to keep testing really easy. The point of this is to make sure props sent down correctly.
import React, { Component } from 'react';
import { graphql } from 'react-apollo';
import { VERIFY_BANK_ACCOUNT } from '../../../queries';
import { BankVerification } from '../../decorators';
class BankVerificationContainer extends Component {
handleVerification = variables => {
const { verifyBankAccount } = this.props;
verifyBankAccount({ variables });
};
render() {
const { style } = this.props;
return (
<BankVerification
style={style}
handleVerification={this.handleVerification}
{...this.props}
/>
);
}
}
const verifyAccount = graphql(VERIFY_BANK_ACCOUNT, { name: 'verifyBankAccount' });
export default verifyAccount(BankVerificationContainer);