ํ๋ณตํ 2์ฃผ์ฐจ ์์
๐ uuid ์ฌ์ฉํ๊ธฐ
React์ ์ ์ผํ key ๊ฐ์ ์ ๊ณตํ๋ ํจํค์ง
React์์์ ์ ์ผํ key prop ๊ฐ์ ๋ฆฌ์กํธ๊ฐ ๋ฆฌ์คํธ ์์ดํ ์ ๊ตฌ๋ถํ ์ ์๊ฒ ํ๋ค.
๊ทธ ์ ์๋ ๋ฐฐ์ด์ key๊ฐ์ ์ธ๋ฑ์ค๋ฅผ ๋ฃ์๋๋ฐ, ๋ฐฐ์ด์ ์ธ๋ฑ์ค๋ 0,1,2,3.. ์ด๋ฏ๋ก ๊ณ ์ ํ ๊ฐ์ด ์๋๋ฏ๋ก React๊ฐ ๋ ๋๋ง์ ์๋ชปํ ์ ์๊ธฐ ๋๋ฌธ์ ๊ณ ์ ํ ๊ฐ์ ๋ฃ์ด์ผ ํ๋ค.
-> ๋ฐฐ์ด์ data ์ถ๊ฐ ์์ ์ ์ ์ฒด ๋ฐฐ์ด์ ๋ค ์์ ํด์ผ ํ๊ธฐ ๋๋ฌธ์
์ ์ฝ๋์์ uuid ํจํค์ง๋ฅผ ์ฌ์ฉํด key๊ฐ์ผ๋ก ์ฌ์ฉํ ์ ์ผํ UUID๋ฅผ ๋ง๋ค์ ์๊ณ ์ด์ฒ๋ผ ์ ์ผํ ID๋ ์ถฉ๋ํ ์ผ๋ ค๊ฐ ๊ฑฐ์ ์์ต๋๋ค.
uuid ๊ฐ์ ํจํค์ง๋ฅผ ์ฌ์ฉํจ์ผ๋ก์จ ID๋ ์ ์ผํ๊ณ ์์ธก๊ฐ๋ฅํด์ง๊ณ , ์ฐ๋ฆฌ๋ ๊ฐ ์์ดํ ์ ๋ํด ์๋ก์ด ID๋ฅผ ์์ฑํ ๋ฐฉ๋ฒ์ ๊ณ ๋ฏผํ ํ์๋ ์์ด์ง๋ค.
๐ ์คํ์ผ ๊ด๋ จ ๊ตฌ์กฐ
๋ง์์... ๋์ !!!!!!!!!
* Styled component๊ฐ ์์ฑ์ props๋ก ์ ๋ฌ๋ฐ์ ๋ ์ type
interface ProfileImgProps {
src: string;
style: {
width: number;
height: number;
};
}
const ProfileImg = ({ src, style }: ProfileImgProps): JSX.Element => {
return <ProfileImgStyle {...{ src, style }}></ProfileImgStyle>;
};
export default ProfileImg;
const ProfileImgStyle = styled.img`
width: ${({ style }) => `${style?.width}px`};
height: ${({ style }) => `${style?.height}px`};
border-radius: 70%;
/* object-fit: contain; */
`;
์ฌ์ฉ ์
<ProfileImg style = {width='20px' height='30px'} />
์ฌ์ค ์ style๋ถ๋ถ์ generic์ ์ฌ์ฉํ๋ฉด ์๋ฒฝ
์ฐธ๊ณ : https://github.com/styled-components/styled-components/issues/630#issuecomment-547484722
๐ ๋ฐ๋ณต๋ component style ์ง์
1) ์ ํด์ง style์ด ์์ ๋ : type์ผ๋ก ๋ฒํผ์ ์ฌ์ฉํ ๋ LabelLargeGroup
์ฅ์ : props ๋ฅผ ๋ฐ์ง ์์์ผ๋ก์จ component๋ด์ ํ ๋ถ๋ถ์ด ๋ณ๊ฒฝ๋๋ ์ ๋ถ ๋ค๋ณ๊ฒฝ
2) ์์ํ style์ด ๊ณ์ ๋ณ๊ฒฝ๋ ๋: ํ๋์ component๋ฅผ ๋ง๋ค์ด์ props๋ก ๋ด๋ ค์ ์ฌ์ฉํ ๊น IssueHeaderButton
1) component๋ฅผ ํ๋์ฉ ๋ง๋ ๊ฒฝ์ฐ
const LabelLargeGroup = ({ type }: Prop): JSX.Element => {
return {
open: <IssueLabelOpen />,
closed: <IssueLabelClosed />,
}[type] as JSX.Element;
};
export default LabelLargeGroup;
const IssueLabelOpen = () => {
return (
<IssueLabelStyle>
<CustomOpenChip
avatar={<OpenIconGroup type={'label'} />}
label={OPEN_ISSUE}
color="primary"
variant="outlined"
/>
</IssueLabelStyle>
);
};
2) props๋ก ์ผ์ผํ ๋ฐ์ ๋
const IssueHeaderButton = ({
icon,
text,
count,
}: IssueHeaderButtonProps): JSX.Element => {
return (
<IssueHeaderButtonStyle>
<IconBox>{icon}</IconBox>
<div>
<TextBox>{text}</TextBox>
<CountBox>{count}</CountBox>
</div>
</IssueHeaderButtonStyle>
);
};
๐ material UI ์
๊ฐ์ฒด๋ฆฌํฐ๋ด๋ก ๊ทธ๋ฃน๋ณ๋ก ๋ค์์คํ์ด์ค๋ฅผ ๋ง๋ค์ด์ ์ฌ์ฉ
๋จ material UI์ ์์ฑ์ผ๋ก ๋ฃ์ด์ค๋๋ ๋ณ์๋ก ์ง์ ์ด ์๋จ
๐ decoded๋ฅผ ๋งค๋ฒ ํ๋ ๊ณผ์ ์ด๋ป๊ฒ ํจ์จ์ ์ผ๋ก ๋ฐ๊ฟจ์๊น?
์์ ์
localStorage.setItem('token', data.token);
const token = localStorage.getItem('token');
if (!token) return;
const decoded =
jwtDecode<{ name: string; profileImageUrl: string }>(token);
localStorage.setItem('name', decoded.name);
localStorage.setItem('profileImageUrl', decoded.profileImageUrl);
history.push(P.ISSUE_LIST);
decoded๋ฅผ ๋ณด๊ดํด๋๊ณ name๊ณผ profileImg๋ก ์ ์ฅ์ ํ๋๋ฐ,
ํด๋น๋ฐฉ๋ฒ์ ์ฌ์ฉ์์ ๊ฐ์ธ์ ๋ณด์ธ ์ด๋ฆ๊ณผ ํ๋กํ์ด๋ฏธ์ง๊ฐ ๋๋ฌด๋ ์ฝ๊ฒ ๋ ธ์ถ๋๊ธฐ ๋๋ฌธ์ ๋ณ๊ฒฝ์ด ํ์ํ๋ค๋ ์กฐ์ธ์ ๋ฐ์๋ค.
์์ ํ
Recoil์ ํ์ฉํ ์ฌ์ฉ์ ์ ๋ณด ํ์ฉ
export const token = atom({
key: 'token',
default: null,
});
export const decodedToken = atom({
key: 'decodedToken',
default: {
name: '',
profileImageUrl: '',
},
});
const decoded = decodedToken && useRecoilValue(decodedToken);
const profileURL = decoded && decoded.profileImageUrl;
token๊ณผ ์ฌ์ฉ์ ์ ๋ณด์ธ name, profileImg์ atom์ผ๋ก ๊ด๋ฆฌํ์ฌ ๊ฐ์ธ์ ๋ณด๋ ธ์ถ์ ํผํ๋ฉด์๋
๋ค์ํ component์์ ํ์์ ๋ฐ๋ผ useRecoilValue๋ฅผ ํตํด ๊บผ๋ด์ ์ฌ์ฉํ๋๋ก ๋ณ๊ฒฝํ๋ค.
์ฝ๋๋ฅผ ๊ตฌํํ๋ ์ ์ฅ์๋ง ์๊ฐํ์ง ์๊ณ ์ฌ์ฉ์ ์ ์ฅ์ผ๋ก ๊ตฌํํด์ผ ํ๋ค๋ ๊ตํ์ ์ป์๋ค.
๐ MaterialUI ๋ด๋ถ ๊ฐ ์์์ Link ๊ฑธ๊ธฐ + ์ค๋ฅ์ ๋๋ฒ๊น ํ๋ ๊ณผ์
index.js:1 Warning: React does not recognize the `disableRipple` prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase `disableripple` instead. If you accidentally passed it from a parent component, remove it from the DOM element.
๋ผ์ฟค๊ณผ ํจ๊ป ์ฌ๋ฌ ์ค๋ฅ๋ค์ ๋ง์ดํ๋ฉฐ ๋จ์ํ ์ค๋ฅ๊ฐ ๋ฌ์ ๋ Warning ์์ ์ค๋ฅ ๋ฉ์ธ์ง๋ฅผ ๋ณต์ฌํด์ ๊ฒ์ํ๋๊ฒ ๋ค์ผ๊น? ๋ผ๋ ์๋ฌธ์ด ๋ค์๋ค. ๊ทธ๋ฌ๋ ์ค ๋ผ์ฟค์ด ๋๋ฒ๊น ์ ํ๊ณ ์ค๋ฅ๋ฅผ ์ฐพ๋ ๊ณผ์ ์ ๋ํด ์ค๋ช ํด์ฃผ์๋๋ฐ, ๊ทธ ๊ณผ์ ๊ณผ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ๋ํด ์ ๋ฆฌํ๊ณ ์ ํ๋ค.
[ โ Warning ๋ฐ ]
* ๋๋ฒ๊น ์์์ ๊ณผ์ ๊ณผ ๋ ๋๋ง ๋๋ ์์๋๋ก ๋ณด์ฌ์ฃผ๋ ๊ฒ
* ์คํ์ ์์ฌ ์๋ ์์๊ฐ ๋ณด์ฌ์ง๋๊ฒ
* ์ค๋ฅ๊ฐ ๋ ์์น๋ฅผ ์๋ ค์ฃผ๋ ๊ฒ
1) ๊ฒฐ๊ตญ ์ค๋ฅ๊ฐ ๋ ๊ณณ a link
2) ๊ทธ๋ผ ์ด๋์ ์๋ ๋งํฌ์ธ๊ฐ?๋ฅผ ๋ณด์ฌ์ฃผ๊ธฐ ์ํด์ ๋ฆฌ์กํธ ์ต์์ ๋ถํฐ ๋ค์ด๊ฐ๋ค (StyleProvider)
3) IssueList / TabGroup / ButtonGroup ๊ณ์ํด์ ๋ฐ๋ผ ๋ค์ด๊ฐ๋ค ๋ณด๋ฉด
4) ButtonGroup๋ด์ ๋งํฌ(a) ์์ ์๋ชป๋์๋ค๋๊ฒ์ ์๊ฒ ๋๋ค.
[๋ฌธ์ ์ํฉ]
์ฌ์ฉํ์ง๋ ์์ MaterialUI์ ๋ด๋ถ์์๋ค์ UpperCase๊ฐ ์๋ชป๋์๋ค๋ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค.
TabGroup
const TabGroup = (): JSX.Element => {
const classes = useStyles();
return (
<div className={classes.root}>
<CustomTabGroup
size="large"
color="primary"
aria-label="large outlined primary button group"
>
<Link to="/main/label-list">
<TabButton startIcon={<LocalOfferIcon />}>
<TextGroup type={T.SMALL} content={TT.LABEL} color="#6E7191" />
<CountGroup count={0} color="#6E7191" />
</TabButton>
</Link>
<Link to="/main/milestone">
<TabButton startIcon={<FlagIcon />}>
<TextGroup type={T.SMALL} content={TT.MILESTONE} color="#6E7191" />
<CountGroup count={0} color="#6E7191" />
</TabButton>
</Link>
</CustomTabGroup>
</div>
);
};
TabGroup์ ํ์ธํด๋ณด๋ฉด <CutsomTabGroup> ์์ <TabButton>์ด ์๊ณ ๊ทธ๊ฒ๋ค์ด <Link>๋ก ๊ฐ์ธ์ ธ ์๋ค.
์ฌ๊ธฐ์ ๊ฐ๊ณผํ ์ ์
<CutsomTabGroup> : Material UI์ ButtonGroup์ด๊ณ
<TabButton> : Material UI์ Button ์ด๋ค ( ButtonGroup์ ๊ฐ์์)
์ฐธ๊ณ :https://material-ui.com/components/button-group/
ํ์ฌ ๋ฌธ์ ๊ฐ ๋๋ ์ฝ๋๋ฅผ ๋ณด๋ฉด Material UI์ ๋ด๋ถ์ ์ ๊ทผํด Link๋ฅผ ๊ฑธ๊ณ ์๊ธฐ ๋๋ฌธ์ ๋ฌธ์ ๊ฐ ๋๋ ๊ฒ์ด์๋ค.
[ ๊ฒฐ๋ก ]
โญ๏ธmaterial UI ๋ด๋ถ ์์์ ๋งํฌ๋ฅผ ๊ฐ๊ฐ ๊ฑธ ์ ์๋ค.
โญ๏ธ Material UI์๋ ๋งํฌ๋ฅผ ๊ฑฐ๋ ๋ฐฉ๋ฒ์ด ๋ฐ๋ก ์๋ค.
[ ํด๊ฒฐ๋ฐฉ์]
1) ButtonGroup์ ์ฌ์ฉํ์ง ์๋๋ค
2) Button์ component๋ก Link๋ฅผ ์ ๋ฌํ๋ค.
https://github.com/nygardk/react-share/issues/316
ํ๊ตฌ ๋์ 2) Button์ component๋ก Link๋ฅผ ์ ๋ฌํ๋ค. ๋ฐฉํฅ์ผ๋ก ๊ฒฐ์ฌํ๊ณ
ReactRouter์์ Link๋ฅผ ๊ฐ์ ธ์์ Button์ component= {Link} ๋ก ์ ๋ฌํ๋ฉด ButtonrGroup๋ ์ฌ์ฉ ๊ฐ๋ฅํ๊ณ ๊ทธ ๋ด๋ถ์ Button์๋ ๊ฐ๊ฐ ๋งํฌ๋ฅผ ๊ฑธ ์๋ ์๊ฒ ๋์๋ค.
๋๊ธ