【React】Atomic Design におけるコンテナー・コンポーネントとプレゼンテーショナル・コンポーネント

[Atomic Design ～堅牢で使いやすいUIを効率良く設計する](https://www.amazon.co.jp/dp/B07CJ5TLK2/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1) のコードを読んで???となったので自分なりのコードリーディングの流れをメモしておきます。

## 1

コードはこっちにもあります。
https://codesandbox.io/s/react-atomic-design-wyr90

console.logの結果は `` <Heading level={6}>見出し</Heading>`` に対する結果です。

```styles.css

.h {
  font-weight: 700;
  line-height: 1.5;
}

.h1 {
  font-size: 2rem;
}
.h2 {
  font-size: 1.8rem;
}
.h3 {
  font-size: 1.6rem;
}
.h4 {
  font-size: 1.4rem;
}
.h5 {
  font-size: 1.2rem;
}
.h6 {
  font-size: 1rem;
}
```

```jsx:index.js
import React from "react";
import ReactDOM from "react-dom";

import Heading from "./Heading";

const rootElement = document.getElementById("root");
ReactDOM.render(
  <>
    <Heading>見出し</Heading>
    <Heading level={6}>見出し</Heading>
  </>,
  rootElement
);
```

```jsx:Heading.js
import React from "react";
import styles from "./styles.css";

// 見た目を表現するという関心
export const HeadingPresenter = function({
  tag: Tag,
  visualLevel,
  className,
  ...props
}) {
  //console.log(Tag); // h6
  //console.log(visualLevel) // 6
  //console.log(className); // undefined
  //console.log(props); // {children: "見出し"}
  return (
    <Tag
      className={[styles.h, styles[`h${visualLevel}`], className].join(" ")}
      {...props}
    />
  );
};

// レベルを決定するという関心
export const HeadingContainer = function({
  presenter,
  level = 2,
  visualLevel,
  ...props
}) {
  //console.log(presenter); // presenter() {}
  //console.log(props) // {children: "見出し"}
  level = Math.max(0, Math.min(6, level));
  visualLevel = typeof visualLevel !== "undefined" ? visualLevel : level;
  const tag = `h${level}`;

  return presenter({ tag, visualLevel, ...props }); // 引数は、presenterPropsでHeadingPresenterを返す
};

const Heading = function(props) {
  //console.log(props);  // {level: 6, chileren: "見出し"}
  return (
    <HeadingContainer
      presenter={function(presenterProps) {
        //console.log(presenterProps) // {tag: "h6", visualLevel: 6, children: "見出し"}
        return <HeadingPresenter {...presenterProps} />;
      }}
      {...props}
    />
  );
};

export default Heading;
```

1. **Headingコンポーネント**が、``index.js``に定義されている。
2. ``{level: 6, chileren: "見出し"}`` が、``props``で、``Heading.js``の**Headig関数**に渡ってきている。
3. **Heading関数**は、**HeadingContainerコンポーネント**を返している。
4. **HeadingContainerコンポーネント**は、**HeadingPresenterコンポーネント**を返す関数を``presenter``という名前で``props``として持っている。また、``level``と``children``も``props``として持っている。
5. **HeadingContainer関数**は、propsとして受け取った**presenter**関数を実行し、**HeadingPresenterコンポーネント**を返す。
6. **HeadingPresenter関数**は、``<h6 children="見出し">``を返し、``children``の中身である``"見出し"``がブラウザ表示されている。

イメージとしては**Container**で色々固有の設定を吸って**Presenter**に吐き出して受け取った**Presenter**が**Tag**を吐き出して表示させる感じ。

## 2

```css:styles.css
.clickable {
    cursor: pointer
}
```

```jsx:index.js
import React from "react";
import ReactDOM from "react-dom";

import { TrashCanIcon } from "./Icon";

const rootElement = document.getElementById("root");
ReactDOM.render(
  <>
    <TrashCanIcon /> // 2
    <TrashCanIcon onClick={ action('アイコンがクリックされました') }/>
  </>,
  rootElement
);
```

```jsx:Icon.js
import React from 'react'
import styles from './styles.css';

// 見た目を表現するという関心
export const IconPresenter = ({
    iconName,
    height = 20,
    width = 20,
    ...props
}) => (
    <img
      src={`/icons/${iconName}.svg`}
      alt=""
      height={height}
      width={width}
      {...props}
    />
)

// アイコンを決定するという関心
export const IconContainer = ({
    presenter,
    onClick,
    className="",
    ...props
}) => {
    if (onClick) className += `${styles.clickable}`
    return presenter({ onClick, className, ...props })
}

export const iconFactory = iconName => props => (
    <IconContainer 
      presenter={ presenterProps => <IconPresenter {...presenterProps} />} 
     {...{ iconName, ...props }}
    />
)

export const TrashCanIcon = iconFactory('trash-can') // 1
export const ChevronRightIcon = iconFactory('chevron-right')
export const SearchIcon = iconFactory('search')
export const SettingsIcon = iconFactory('settings')
```

[3歳娘「パパ、関数をカリー化して？」](https://qiita.com/Yametaro/items/99cc1c8ebcfc703b1410)

1. 部分適用し、返ってきた関数を定数に入れて保存
2. 呼び出す