DEV Community

Software Engineer
Software Engineer

Posted on

How to make component like <card.header>

Top comments (3)

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

Collapse
 
dance2die profile image
Sung M. Kim

Hi @wmgstar ,

Would you double check if the post was created successfully?