A brief overview of react and its component architecture
React was heavily inspired by functional programming.
The laws of functional programming:
Component architecture is very popular in the web world.
A component that contains some logic, usually in charge of a CRUD operation on some level.
The presentation layer. No logic allowed, only pure, stateless components.
Breaking apart logic into separate components means you often times have to double the amount of components you need to build.
Component trees can get messy and hard to manage.
Often, logic is bundled together inside one component. Meaning your components no longer follow the single responsibility principle.
Logic can actually be separated much easier with hooks:
// Logic
export default function useInput(value) {
const [state, setState] = useState(value)
const handleInputChange = ({ target: { value } }) => {
setState(value)
}
return [state, handleInputChange, setState]
}
// Presentation
function InputComponent() {
const [state, handleOnChange] = useInput('')
return (
<Input
onChange={handleOnChange}
value={state}
/>
)
}function CandyTable({ flavorID }) {
// Logic
const [{ fetching, data }] = useCandyQuery(getFlavorQuery(flavorID))
// Display
if (fetching) {
return (<Loading />)
}
return (
<Card small className="mb-4">
<table className="table mb-0">
<thead className="bg-light styled-header">
<tr>
<th scope="col" className="border-0">
Candy Name
</th>
<th scope="col" className="border-0">
Flavor
</th>
</tr>
</thead>
<tbody>
{
data.map((item) => {
return (
<CandyItem
key={`${item.id}-candy-item`}
{...item}
/>
)
})
}
</tbody>
</table>
</Card>
);
}There are three (main) kinds of errors
(in javascript)
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error, info) {
// You can also log the error to an error reporting service
logErrorToMyService(error, info);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}A JavaScript error in a part of the UI shouldn’t break the whole app.
Error boundaries do:
Catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the component tree that crashed.
Catch errors during rendering, in lifecycle methods, and in constructors of the whole tree below them.
Error boundaries do not catch errors for:
Error boundaries are not wrappers for every component
Error boundaries do not catch errors for:
Error boundaries are not wrappers for every component
Best Practices
While this error boundary catches the buggy chat component, it unmount the entire children tree.
ErrorBoundary
ChatContext
ChatWindow
ErrorBoundary
ChatMessage
MessageContainer
ErrorBoundary
BuggyChatComponent
This error boundary catches the BuggyChatComponent,
but requires that almost every component be wrapped.
ErrorBoundary
ChatContext
ErrorBoundary
ChatWindow
ChatMessage
ChatMessage
ErrorBoundary
MessageContainer
BuggyChatComponent
An Error boundary works many levels deep, so there’s no need to “wrap at every level”. Just put your <ErrorBoundary> in a few strategic places.
- Dan
Craft messages that are easy to understand and which are actionable
Log pertinent application state and recent user activity
Automate recovery wherever possible
Allow the user to retry actions
Sometimes, a fallback component isn't the right answer.
Do we want to automate github ticket creation for our production environment?