🐖
REACT Component内のイベントハンドラ留意事項
概要
- REACT + Typescriptフロントエンド環境でAtomic Designの考え方を踏襲し、ボタン等のコンポーネントを最小単位のAtomsとして作成した。
- 作成したAtomsのボタンコンポーネントをインポートし、onClickなどのイベントハンドラ呼び出しをしても機能しない。
- onClickがpropsとして扱われている。
- onClickなどのイベントハンドラを機能させるためには、propsが引き渡された子コンポーネント先で実行するのが正解だと思われる。
既存コード
- ロジックとView分離のためカスタムフック化した関数を呼び出し、コンポーネント内でonClickイベントハンドラをトリガーに関数を実行。
App.tsx
import { useCount } from "../src/hooks/useCount";
import "./styles.css";
export default function App() {
const { count, onClickCountUp } = useCount();
return (
<div className="App">
{count}
<br />
<button onClick={onClickCountUp}>CountUp</button>
</div>
);
}
../src/hooks/useCount.tsx (カスタムフック)
import { useCallback, useState } from "react";
export const useCount = () => {
const [count, setCount] = useState(0);
const onClickCountUp = useCallback(() => setCount(count + 1), [
count,
setCount
]);
return { count, setCount, onClickCountUp };
};
ATOMSコンポーネントを適用
- ここで、ボタンにATOMSでデザインしたPrimaryButtonコンポーネントを適用する。
- エラー:Property 'onClick' does not exist on type 'IntrinsicAttributes & { children?: ReactNode; }'.ts(2322) が発生する。
- <PrimaryButton onClick={onClickCountUp}>のonClickがpropsとして認識され、子コンポーネントへ渡そうとしている。
App.tsx
import { useCount } from "../src/hooks/useCount";
import { PrimaryButton } from "../src/components/atoms/button/PrimaryButton";
import "./styles.css";
export default function App() {
const { count, onClickCountUp } = useCount();
return (
<div className="App">
{count}
<br />
<PrimaryButton onClick={onClickCountUp}>CountUp</PrimaryButton>
</div>
);
}
../src/components/atoms/button/PrimaryButton.tsx (Atomsコンポーネント)
import { FC, memo, ReactNode } from "react";
import styled from "styled-components";
const SButton = styled.button`
color: #ffffff;
background-color: #cc6633;
padding: 8px 40px;
border: none;
border-radius: 1000px;
outline: none;
&:hover {
cursor: pointer;
opacity: 0.7;
}
`;
type Props = {
children: ReactNode;
};
export const PrimaryButton: FC<Props> = memo((Props) => {
const { children } = Props;
return <SButton>{children}</SButton>;
});
修正したコード
- propsが引き渡された先の子コンポーネントで、関数として受け取り実行する。
- カスタムフック化して関数を受け取り、さらに受け取った関数を実行先の子コンポーネントへ引き渡している。
- onClickイベントの無いbuttonタグはほぼ考えられないため、ATOMSコンポーネントとして機能しているので、一旦はこれで良しとする。(本当かな。。)
App.tsx
import { useCount } from "../src/hooks/useCount";
import { PrimaryButton } from "../src/components/atoms/button/PrimaryButton";
import "./styles.css";
export default function App() {
const { count, onClickCountUp, onClickCountDown } = useCount();
return (
<div className="App">
{count}
<br />
<PrimaryButton onClick={onClickCountUp}>CountUp</PrimaryButton>
</div>
);
}
../src/components/atoms/button/PrimaryButton.tsx (Atomsコンポーネント)
import { FC, memo, ReactNode } from "react";
import styled from "styled-components";
const SButton = styled.button`
color: #ffffff;
background-color: #cc6633;
padding: 8px 40px;
border: none;
border-radius: 1000px;
outline: none;
&:hover {
cursor: pointer;
opacity: 0.7;
}
`;
type Props = {
children: ReactNode;
onClick: () => void;
};
export const PrimaryButton: FC<Props> = memo((Props) => {
const { children, onClick } = Props;
return <SButton onClick={onClick}>{children}</SButton>;
});
Discussion