2 minute read

学んだこと

AtomicDesign のまとめ

  • あくまでベースとして考える
    • プロジェクトやチームに合わせてカスタマイズする。
  • 初めから AtomicDesign で分けないようにする
    • 慣れない内に無理にコンポーネントを分けると、自分自信が大変になる。
    • まずは処理を書いてから定期的にリファクタリングすることが必要。
  • 要素の関心を意識する
    • 何に関心があるコンポーネントなのかを意識しながら分割したり props を定義したりする。

グローバルな state について

  • 画面のどのコンポーネントからでも参照できる値と、その値をそのページからでも変更することができる。
  • C++や C#などにあったグローバル変数と同じようなもの。
  • useState は、特定のコンポーネント内でしか変更することが出来ない。
    • 規模が大きくなると、グローバルに持たせたい欲が出る。

グローバルな state がなぜ必要なのか前回作成したページを使って確認する

  • 例えば、管理者でユーザ情報を削除したい場合
    • 管理者かユーザか判定し管理者であれば、削除ボタンを表示し、ユーザであれば何も出さない処理を追加したい。
      • その場合、各コンポーネントに管理者か管理者ではないフラグを渡す必要がある。
      • 正直複数コンポーネントを触るため、修正が大変になる。
    • グローバルな state があれば、その state をボタンを表示しているコンポーネントで呼び出す事で修正することができる。

グローバルな state が必要なのか、state で管理しない実装をしてみて確認してみる

  • 管理者ではユーザの削除ボタンを表示し、一般ユーザはユーザの削除ボタンを表示しないようにする。

コンポーネント一覧

  • top.jsx
    • トップページ表示用コンポーネント
    • ここで state 内に isAdmin フラグを用意する。
  • User.jsx
    • state 内の isAdmin フラグを受け取る
    • UserCard.jsx
      • ユーザ情報をまとめたコンポーネント
      • 受け取った isAdmin フラグを UserIconWithName コンポーネントに渡す。
      • UserIconWithName.jsx
        • ユーザアイコンとユーザ名を表示するコンポーネント
        • isAdmin フラグ確認し、true であれば編集ボタンを表示。
        • false であれば編集ボタンを表示しない。

Top.jsx

import styled from 'styled-components';
import { SecondaryButton } from '../atoms/button/SecondaryButton';
import { useHistory } from 'react-router-dom';

export const Top = () => {
  const history = useHistory();
  const onClickAdmin = () =>
    history.push({ pathname: '/users', state: { isAdmin: true } });
  const onClickGeneral = () =>
    history.push({ pathname: '/users', state: { isAdmin: false } });

  return (
    <SContainer>
      <h2>TOPページです</h2>
      <SecondaryButton onClick={onClickAdmin}>管理者</SecondaryButton>
      <SecondaryButton onClick={onClickGeneral}>一般ユーザ</SecondaryButton>
    </SContainer>
  );
};

const SContainer = styled.div`
text-align: center`;

User.jsx

import styled from 'styled-components';
import { SearchInput } from '../molecules/SearchInput';
import { UserCard } from '../organisms/user/UserCard';
import { useLocation } from 'react-router-dom';

export const Users = () => {
  const { state } = useLocation();
  const isAdmin = state ? state.isAdmin : false;
  return (
    <SContainer>
      <SUserArea>
        <h2>ユーザ一覧</h2>
        <SearchInput />
        {users.map((user) => (
          <UserCard key={user.id} user={user} isAdmin={isAdmin} />
        ))}
      </SUserArea>
    </SContainer>
  );
};
const users = [...Array(10).keys()].map((val) => {
  return {
    id: val,
    name: `あああ${val}`,
    image:
      'https://images.unsplash.com/photo-1598133894008-61f7fdb8cc3a?q=80&w=988&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',
    email: '12345@example.com',
    phone: '000-0000-0000',
    company: {
      name: 'テスト株式会社',
    },
    website: 'https://google.com',
  };
});

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;
`;

UserCard.jsx

import styled from 'styled-components';
import { Card } from '../../atoms/card/Card';
import { UserIconWithName } from '../../molecules/user/UserIcomWithName';

export const UserCard = (props) => {
  const { user, isAdmin } = props;
  return (
    <Card>
      <UserIconWithName image={user.image} name={user.name} isAdmin={isAdmin} />
      <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;
  dt{
    float: left;
  }
  dd{
    padding-left: 32px;
    padding-bottom: 8px;
    overflow-wrap: break-word;
  }
`;

UserIconWithName.jsx

import styled from 'styled-components';

export const UserIconWithName = (props) => {
  const { image, name, isAdmin } = props;
  return (
    <SContainer>
      <SImg height={160} width={160} src={image} alt={name} />
      <SName>{name}</SName>
      {isAdmin && <SEdit>編集</SEdit>}
    </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: #40514;
`;

const SEdit = styled.span`
text-decoration: underline;
color: #aaa;
cursor: pointer;`;

実装してみて

  • 複数のコンポーネントを何個も修正する必要があり、結構面倒だと思った。
  • 実装中に props で isAdmin を受け取り忘れて、管理者のボタンを押しても編集ボタンが表示されなかった。
    • そのため、ケアレスミスや実装し忘れなど起きやすい。

Tags:

Updated: