cossack910 / AtomicDesign

0 stars 0 forks source link

メモ化した子コンポーネントが再レンダリングされる #9

Open cossack910 opened 1 year ago

cossack910 commented 1 year ago

親コンポーネントからpropsとしてオブジェクトを渡していたことが原因

子コンポーネントであるUserCardコンポーネント内のcompanyオブジェクトが毎回生成されていたので無駄な再レンダリングが起こっていた。

Users.tsx

import React, { memo } from "react";
import styled from "styled-components";
import SearchInput from "../molecule/SearchInput";
import users from "./TestData";
import UserCard from "../organisms/user/UserCard";
import SecondaryButton from "../atoms/button/SecondaryButton";
import { useUserContext } from "../../providers/UseUserContext";

export const Users: React.FC = memo(() => {
  const { userInfo, setUserInfo } = useUserContext();
  const onClickSwicth = () => setUserInfo({ isAdmin: !userInfo.isAdmin });
  console.log("rendering Users");
  return (
    <SContainer>
      <h2>ユーザー一覧</h2>
      <SearchInput />
      <br />
      <SecondaryButton text="権限切り替え" onClick={onClickSwicth} />
      <SUSreArea>
        {users.map((user) => (
          <UserCard
            key={user.id}
            name={user.name}
            image={user.image}
            email={user.email}
            phone={user.phone}
            company={{name: user.company.name}} ←これダメ
            website={user.website}
          />
        ))}
      </SUSreArea>
    </SContainer>
  );
});

const SContainer = styled.div`
  text-align: center;
  flex-direction: column;
  align-items: center;
  padding: 24px;
`;

const SUSreArea = styled.div`
  padding-top: 40px;
  width: 100%;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  grid-gap: 20px;
`;

export default Users;

UserCard.tsx

import React, { memo } from "react";
import styled from "styled-components";
import Card from "../../atoms/card/card";
import UserIconWithName from "../../molecule/user/UserIconWithName";

interface UserCardProps {
  name: string;
  image: string;
  email: string;
  phone: string;
  company: {
    name: string;
  };
  website: string;
}

export const UserCard: React.FC<UserCardProps> = memo(
  ({ name, image, email, phone, company, website }) => {
    console.log("redering  UserCard");
    return (
      <Card>
        <UserIconWithName name={name} image={image} />
        <SDL>
          <dt>メール</dt>
          <dd>{email}</dd>
          <dt>TEL</dt>
          <dd>{phone}</dd>
          <dt>会社名</dt>
          <dd>{company.name}</dd>←こいつ
          <dt>ウェブサイト</dt>
          <dd>{website}</dd>
        </SDL>
      </Card>
    );
  }
);

const SDL = styled.dl`
  text-align: left;
  margin-bottom: 0px;
  dt {
    float: left;
  }
  dd {
    padding-left: 32px;
    padding-bottom: 32px;
    overflow-wrap: break-word;
  }
`;

export default UserCard;

TestData.ts

export const TestData = [...Array(10).keys()].map((val) => {
  return {
    id: val,
    name: `test1${val}`,
    image: "https://source.unsplash.com/Sg3XwuEpybU",
    email: "aaa@aaa.com",
    phone: "090-8888-7777",
    company: { name: "aaaaa" },
    website: "test@kaisya.com",
  };
});

export default TestData;
cossack910 commented 1 year ago

修正後

Users.tsx

import React, { memo } from "react";
import styled from "styled-components";
import SearchInput from "../molecule/SearchInput";
import users from "./TestData";
import UserCard from "../organisms/user/UserCard";
import SecondaryButton from "../atoms/button/SecondaryButton";
import { useUserContext } from "../../providers/UseUserContext";

export const Users: React.FC = memo(() => {
  const { userInfo, setUserInfo } = useUserContext();
  const onClickSwicth = () => setUserInfo({ isAdmin: !userInfo.isAdmin });
  console.log("rendering Users");
  return (
    <SContainer>
      <h2>ユーザー一覧</h2>
      <SearchInput />
      <br />
      <SecondaryButton text="権限切り替え" onClick={onClickSwicth} />
      <SUSreArea>
        {users.map((user) => (
          <UserCard
            key={user.id}
            name={user.name}
            image={user.image}
            email={user.email}
            phone={user.phone}
            company={user.company.name}
            website={user.website}
          />
        ))}
      </SUSreArea>
    </SContainer>
  );
});

const SContainer = styled.div`
  text-align: center;
  flex-direction: column;
  align-items: center;
  padding: 24px;
`;

const SUSreArea = styled.div`
  padding-top: 40px;
  width: 100%;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  grid-gap: 20px;
`;

export default Users;

UserCard.tsx

import React, { memo } from "react";
import styled from "styled-components";
import Card from "../../atoms/card/card";
import UserIconWithName from "../../molecule/user/UserIconWithName";

interface UserCardProps {
  name: string;
  image: string;
  email: string;
  phone: string;
  company: string;
  website: string;
}

export const UserCard: React.FC<UserCardProps> = memo(
  ({ name, image, email, phone, company, website }) => {
    console.log("rendering  UserCard");
    return (
      <Card>
        <UserIconWithName name={name} image={image} />
        <SDL>
          <dt>メール</dt>
          <dd>{email}</dd>
          <dt>TEL</dt>
          <dd>{phone}</dd>
          <dt>会社名</dt>
          <dd>{company}</dd>
          <dt>ウェブサイト</dt>
          <dd>{website}</dd>
        </SDL>
      </Card>
    );
  }
);

const SDL = styled.dl`
  text-align: left;
  margin-bottom: 0px;
  dt {
    float: left;
  }
  dd {
    padding-left: 32px;
    padding-bottom: 32px;
    overflow-wrap: break-word;
  }
`;

export default UserCard;