Layout Components in React part 1 : Split Screens

ReactReact.jsReact Patterns

Monday, April 1, 2024

The concept underpinning layout components revolves around segregating layout styles into their own distinct component, subsequently allowing for the straightforward display of the component itself. This approach facilitates a separation of concerns, enhancing the flexibility of styles. The fundamental principle guiding this strategy is ensuring that "Our components shouldn't know where they're being displayed."

Essentially, instead of the conventional structure:

<div styles={...}>
    <h1>Component Code...</h1>
</div>

We adopt a more modular approach:

<div styles={...}>
    {children}
</div>
<>
    <h1>Component Code...</h1>
</>

By restructuring in this manner, we achieve a cleaner, more adaptable codebase, aligning with best practices in component design.

Consider a component called 'SplitScreen' :

import React from 'react';
import styled from 'styled-components'

const Container = styled.div`
    display: flex;
`

const Pane = styled.div`
    flex: ${props => props.weight};
`
export default function SplitScreen({
                                        left: Left,
                                        right: Right,
                                        rightWeight = 1,
                                        leftWeight = 1
                                    }) {
    return (
        <Container>
            <Pane weight={leftWeight}>
                <Left/>
            </Pane>
            <Pane weight={rightWeight}>
                <Right/>
            </Pane>
        </Container>
    );
};

And here's how you'd use it within your App component:

import './App.css';
import SplitScreen from "./SplitScreen";

const LeftHandComponent = () => {
    return <h1 style={{backgroundColor: "red"}}>Left</h1>;
}

const RightHandComponent = () => {
    return <p style={{backgroundColor: "green"}}>Right</p>;
}


function App() {
    return (
        <SplitScreen leftWeight={1} rightWeight={3}
                     left={LeftHandComponent} right={RightHandComponent}/>
    );
}

export default App;

The issue with the preceding approach lies in its lack of flexibility and reusability, which isn't very developer-friendly. By utilizing layout components, we can enhance these aspects. By treating components as children, we can eliminate props from the component and instead include them as a children prop. Consequently, within the component itself, we can unpack the array of children and map them, ensuring the styles of the children remain reusable and flexible. Additionally, this approach allows us to directly add properties to our LeftHandComponent and RightHandComponent without passing them through the SplitScreen component, as demonstrated below:

import React from 'react';
import styled from 'styled-components'

const Container = styled.div`
    display: flex;
`

const Pane = styled.div`
    flex: ${props => props.weight};
`
export default function SplitScreen({
                                        children,
                                        rightWeight = 1,
                                        leftWeight = 1
                                    }) {

    const [left, right] = children;
    return (
        <Container>
            <Pane weight={leftWeight}>
                {left}
            </Pane>
            <Pane weight={rightWeight}>
                {right}
            </Pane>
        </Container>
    );
};

And within your App component:

import './App.css';
import SplitScreen from "./SplitScreen";

const LeftHandComponent = ({message}) => {
    return <h1 style={{backgroundColor: "red"}}>{message}</h1>;
}

const RightHandComponent = ({name}) => {
    return <p style={{backgroundColor: "green"}}>{name}</p>;
}


function App() {
    return (
        <SplitScreen leftWeight={1} rightWeight={3}>
            <LeftHandComponent message={"Hello"}/>
            <RightHandComponent name={"Right"}/>
        </SplitScreen>
    );
}

export default App;