- Build the UI out of small, modular React components as much as possible.
- Build two types of components: "customizable" ones that only compose others using JSX and contain little-or-no HTML nor logic, as well as "internal" components that contain logic and detailed HTML and CSS, etc.
- In customizable components, include placeholders like
{this.extraContent}
in the render() method so that subclasses don't have to override render().
Bad example:
class Header extends React.PureComponent<Props, State> {
public render() {
return (
<header>
<div className="logoArea">
<img src={this.props.brandedLogo} alt={this.props.siteName} />
</div>
<div className="mainNav">
<ul>
<li>
<a href="/">Home</a>
{this.props.isLoggedIn ? null : <a href="/login">Login</a>}
</li>
</ul>
</div>
<div class="currentUserAvatar">
<!-- User avatar, account menu etc. -->
</div>
</header>
);
}
}
Good example:
// Customizable Header
class _Header extends React.PureComponent<Props, State> {
public render() {
return (
<header>
<SiteLogo/>
<MainNav/>
<UserAvatar/>
</header>
);
}
}
// Customizable Main Navigation Area
class _MainNav extends React.PureComponent<Props> {
public render() {
return (
<MainNavWrapper>
<a href="/">Home</a>
<LoginLink/>
{this.extraNavLinks}
</MainNavWrapper>
);
}
get extraNavLinks(): JSX.Element[] { return []; }
}
// Internal MainNavWrapper - not meant to be modified in most cases)
class _MainNavWrapper extends React.PureComponent<Props> {
public render() {
return <div className="mainNav">
<ul>
{React.Children.map(this.props.children, (child) => (child ? <li>{child}</li> : null))}
</ul>
</div>;
}
}
// Default Theme:
export const Header = _Header;
export const MainNav = _MainNav;
export const MainNavWrapper = _MainNavWrapper;
And here's an example of a custom theme:
class MyThemedHeader extends _Header {
public render() {
return (
<header>
{/* Replace <SiteLogo/> with a fancy widget */}
<MyCustomAnimatedLogoWidget/>
<MainNav/>
<UserAvatar/>
</header>
);
}
}
// Customizable Main Navigation Area
class MyThemedNav extends _MainNav {
get extraNavLinks() {
return [
<a href="/about">About Us</a>,
];
}
}
// My theme:
export const Header = MyThemedHeader;
export const MainNav = MyThemedNav
export const MainNavWrapper = _MainNavWrapper;
I've left out all the Redux glue code, etc., but you can get the idea.
@mtyaka I didn't get a notification about your comment ^ for some reason, but I agree. Thanks for the additional ideas.