以下のUdemyの講座を受講したので、学んだことをまとめていく。
AtomicDesign
画面要素を5つに分割し、組み合わせることでUIを実現する方法。
次の5つの要素から成る。
Atom
最も小さく、それ以上分解できない要素。
ボタン、テキストボックス、アイコンなど。
Atomの例。
PrimaryButton.jsx (Atom)
import styled from 'styled-components';
import { BaseButton } from './BaseButton';
export const PrimaryButton = (props) => {
const { children } = props;
return <SButton>{children}</SButton>;
};
const SButton = styled(BaseButton)`
background-color:#40514e;
`;
BaseButton.jsx (Atom)
import styled from 'styled-components';
export const BaseButton = styled.button`
color: #fff;
padding: 6px 24px;
border: none;
border-radius: 9999px;
outline: none;
&:hover {
cursor: pointer:
opacity: 0.8;
}
`;
Molecule
Atomの組み合わせで意味を持つデザインパーツ。
アイコン+メニュー名、プロフィール画像+テキストボックスなど
Moleculeの例。
SearchInput.jsx (Molecule)
import styled from 'styled-components';
import { PrimaryButton } from '../atoms/button/PrimaryButton';
import { Input } from '../atoms/input/input';
export const SearchInput = () => {
return (
<SContainer>
<Input placeholder="検索条件を入力" />
<SButtonWrapper>
<PrimaryButton>検索</PrimaryButton>
</SButtonWrapper>
</SContainer>
);
};
const SContainer = styled.div`
display: flex;
align-items: center;
`;
const SButtonWrapper = styled.div`
padding-left: 8px;
`;
Organism
AtomやMoleculeの組み合わせで構成される、単体である程度の意味を持つ要素群。
ツイート入力エリア、サイドメニューなど。
Organismの例。
UserCard.jsx (Organism)
import styled from 'styled-components';
import { Card } from '../../atoms/card/Card';
import { UserIconWithName } from '../../molecules/user/UserIconWithName';
export const UserCard = (props) => {
const { user } = props;
return (
<Card>
<UserIconWithName image={user.image} name={user.name} />
<SDL>
<dt>メール</dt>
<dd>{user.email}</dd>
<dt>TEL</dt>
<dd>{user.phone}</dd>
<dt>会社名</dt>
<dd>{user.company.name}</dd>
<dt>WEB</dt>
<dd>{user.website}</dd>
</SDL>
</Card>
);
};
const SDL = styled.dl`
text-align: left;
margin-bottom: 0px;
dt {
float: left;
}
dd {
padding-left: 32px;
padding-bottom: 8px;
overflow-wrap: break-word;
}
`;
Card.jsx (Atom)
import styled from 'styled-components';
export const Card = (props) => {
const { children } = props;
return <SCard>{children}</SCard>;
};
const SCard = styled.div`
background-color: #fff;
box-shadow: #ddd 0px 0px 4px 2px;
border-radius: 8px;
padding: 16px;
`;
UserNameWithIcon.jsx (Molecule)
import styled from 'styled-components';
export const UserIconWithName = (props) => {
const { image, name } = props;
return (
<SContainer>
<SImg height={160} widht={160} src={image} alt={name} />
<SName>{name}</SName>
</SContainer>
);
};
const SContainer = styled.div`
text-align: center;
`;
const SImg = styled.img`
border-radius: 50%;
`;
const SName = styled.p`
font-size: 18px;
font-weight: bold;
margin: 0;
color: #40514e;
`;
Template
ページのレイアウトのみを表現する要素。
実際のデータは持たない。
サイドメニュー、ツイートエリア、トピックエリアなどのレイアウト情報など。
Templateの例。
HeaderOnly.jsx (Template)
import { Header } from "../atoms/layout/Header";
export const HeaderOnly = (props) => {
const { children } = props;
return (
<>
<Header />
{children}
</>
);
};
Header.jsx (Atom)
import { Link } from 'react-router-dom';
import styled from 'styled-components';
export const Header = () => {
return (
<SHeader>
<SLink to="/">HOME</SLink>
<SLink to="/users">USERS</SLink>
</SHeader>
);
};
const SHeader = styled.header`
background-color: #11999e;
color: #fff;
text-align: center;
padding: 8px 0;
`;
const SLink = styled(Link)`
margin: 0 8px
`;
Page
最終的に表示される画面。
Pageの例。
Users.jsx (Page)
import styled from 'styled-components';
import { SearchInput } from '../molecules/SearchInput';
import { UserCard } from '../organisms/user/UserCard';
const users = [...Array(10).keys()].map((val) => {
return {
id: val,
name: `Name${val}`,
image: 'https://source.unsplash.com/NE0XGVKTmcA',
email: '12345@example.com',
phone: '090-1234-5678',
company: {
name: 'テスト株式会社',
},
website: 'https://google.com',
};
});
export const Users = () => {
return (
<SContainer>
<h2>ユーザー一覧</h2>
<SearchInput />
<SUserArea>
{users.map((user) => (
<UserCard key={user.id} user={user} />
))}
</SUserArea>
</SContainer>
);
};
const SContainer = styled.div`
display: flex;
flex-direction: column;
align-items: center;
padding: 24px;
`;
const SUserArea = styled.div`
padding-top: 40px;
width: 100%;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
grid-gap: 20px;
`;
実際にやってみて感じた点、意識すべき点
- コンポーネントを作成したら、都度正しく表示されることを確認してから中身を記述する
- 5つの要素それぞれを格納するディレクトリを作成すると良い
- 使いまわしがきくように可変要素はpropsで渡す
- 他のコンポーネントを囲むように使うコンポーネントは、要素をまるごと渡せるように
children
で受け取るようにする - スタイルは個別の要素に含め、ファイル単体で完結するようにするとメンテしやすい
- スタイルの共通要素は別に切り出すと使いまわししやすい
- 初めから分けずにある程度作ってからリファクタリングする方が良い
- 「何に関心があるコンポーネントなのか」を意識する
- propsで受け取るものを明記して役割を明確にする