DEV Community

Discussion on: How to make component like <card.header>

Collapse
 
dance2die profile image
Sung M. Kim

You first need a top-level Card component.
You can declare a static properties named Header and Body (Capitalize them to adhere to React guideline)

You can follow along here.

class Card extends Component {
  static Header;
  static Body;
  render() {
    return (...);
  }
}

Then you need to implement those two static properties.

Card.Header = ({ children }) => {...};
Card.Body = ({ children }) => {...};

If you need to nest those Card.Header/Body components, you need to use a context to pass top-level values available.

So Card would look like this,

const CardContext = createContext();

class Card extends Component {
  static Header;
  static Body;

  setName = name => this.setState({ name });
  state = {
    name: "Default Card Name",
    setName: this.setName
  };

  render() {
    return (
      <CardContext.Provider value={this.state}>
        {this.props.children}
      </CardContext.Provider>
    );
  }
}

And your Header/Body can access Card value using either useContext hook or as a render prop.

// Using React Hooks (available from v16.8.0 and on)
Card.Header = ({ children }) => {
  const ctx = useContext(CardContext);
  return <h1>{children || ctx.name}</h1>;
};
Card.Body = ({ children }) => {
  const ctx = useContext(CardContext);
  return (
    <section>
      <h2>Card Name: {ctx.name}</h2>
      {children}
    </section>
  );
};

// Using Render Props (before & excluding v16.8.0)
Card.Header = ({ children }) => {
  return (
    <CardContext.Consumer>
      {ctx => <h1>{children || ctx.name}</h1>}
    </CardContext.Consumer>
  );
};
Card.Body = ({ children }) => {
  return (
    <CardContext.Consumer>
      {ctx => (
        <section>
          <h2>Card Name: {ctx.name}</h2>
          {children}
        </section>
      )}
    </CardContext.Consumer>
  );
};

Now you can use Card/Header/Body like following and can also nest inside other elements.

function App() {
  return (
    <div className="App">
      <Card>
        <Card.Header />
        <br />
        <Card.Header>Custom Header</Card.Header>
        <article style={{ margin: "5rem" }}>
          <Card.Body>
            <h3>This is card body</h3>
          </Card.Body>
        </article>
      </Card>
    </div>
  );
}

If you have an Egghead.io account, Kent C. Dodds has an Advaned React Component Patterns course, which discusses this technique in detail.

Collapse
 
wmgstar profile image
Software Engineer

Thanks, Looks good