-
Notifications
You must be signed in to change notification settings - Fork 14
Presentational and Container Components
A common pattern in both the vanilla React and the Redux communities is to divide your code into Presentational and Container components. There is a good descriptions of how this works in this article by Flavio Copes. Essentially, a Presentational component is purely concerned with UI structure and displaying information passed to it as props, whereas a Container component is concerned with internal logic, side effects, communication with Redux etc, and may not render any DOM at all.
This pattern is not an obligation, particularly now that we can also abstract shared component logic via custom hooks- see the updated note on this blog post on the topic by Dan Abramov. However it can be a useful way to separate out UI components that may be changed or A/B tested against alternatives from the underlying application logic that will change less often.
For example, we might decide to run an A/B test showing users one of two different UI components explaining the impact of their contribution. We could write the two UI components to simply accept the same props:
type ContributionImpactProps = {
contributionType: ContributionType;
amount: number;
}
function FeedAHungryJournalist(props: ContributionImpactProps) {
// UI goes here
}
function SupportInvestigations(props: ContributionImpactProps) {
// UI goes here
}
and then have a single container component which is concerned with talking to Redux and determining which variant to display:
function ContributionImpactContainer() {
const contributionType = useSelector(getContributionType);
const contributionAmount = useSelector(getContributionAmount);
const testGroup = useSelector(state => state.common.abParticipations.contributionImpact);
if (testGroup === 'variant') {
return (
<FeedAHungryJournalist
contributionType={contributionType}
amount={contributionAmount}
/>
)
}
return (
<SupportInvestigations
contributionType={contributionType}
amount={contributionAmount}
/>
)
}
If a component needs to retrieve data from or dispatch actions to Redux it may make sense to create a container component to handle this behaviour, in order to facilitate easier UI development and testing- keeping UI components disconnected from Redux makes it easier to work with them in Storybook. Similarly, if the same UI component needs to be backed with different logic based on the application state- for example, handling our different Stripe accounts for one-off and recurring contributions with the same card input form- it makes sense to have separate containers to handle the logic which both render the same UI.
If a component requires access to shared logic that is purely internal- for example, the display control logic for tabs components- it may make more sense to extract that logic into a custom hook rather than a container component. This helps to keep the component tree a little simpler.
- Redux Glossary
- Why Redux Toolkit?
- Writing state slices with Redux Toolkit
- Handling action side effects in Redux
- Presentational and Container Components
- Scoped actions and reducers
- Server Side Rendering
- Form validation
- CI build process
- Post deployment testing
- Post deployment test runbook
- TIP Real User Testing
- Code testing and validation
- Visual testing
- Testing Apple Pay locally
- Test Users
- Deploying to CODE
- Automated IT tests
- Deploying Fastly VCL Snippets
- Archived Components
- Authentication
- Switchboard
- How to make a fake contribution
- The epic and banner
- Environments
- Tech stack
- Supported browsers
- Contributions Internationalisation
- Payment method internationalisation in Guardian Weekly
- Print fulfilment/delivery
- Updating the acquisitions model
- Runscope testing
- Scala Steward for dependency management
- Alarm Investigations
- Ticker data
- Ophan
- Quantum Metric
- [Google Tag Manager] (https://github.com/guardian/support-frontend/wiki/Google-Tag-Manager)