일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 리액트
- KDT 프로그래머스 데브코스 프론트엔드
- vuex map
- intersection opserver
- SCSS use
- 다른컴퓨터에서 git사용
- 프로그래머스 데브코스 프론트엔드
- 이벤트 수식어
- SCSS forward
- SCSS extend
- Vue
- netlify redirect
- SCSS import
- nextjs사용법
- Spacer
- flex
- 리스트 렌더링
- 프로그래머스 데브코스
- 프로그래머스 프론트엔드 데브코스
- 쌓임맥락
- vue mixin
- 고양이 사진 검색기
- react next
- vue 이벤트 수신
- postcss
- git 같은계정 다른 컴퓨터
- vue 지역 컴포넌트
- 프로그래머스 K_Digital Training
- KDT 프로그래머스
- 폼 입력 바인딩
- Today
- Total
혼자 적어보는 노트
프로그래머스 데브코스 TIL - Day 50 본문
✅ 오늘의 학습
📌 React (3)
- 컴포넌트 연습하기
Image 컴포넌트
intersection observer를 사용하여 viewport에 이미지가 나타날 때
네트워크에 이미지 요청을 하는 방식을 강의에서 다루었다.
image 컴포넌트의 기본 형태
export const Image = ({ lazy, threshold = 0.5, placeholder, src, block, width, height, alt, mode, ...props }) => {
const [loaded, setLoaded] = useState(false);
const imgRef = useRef(null);
const imageStyle = {
display: block ? "block" : "undefined",
width,
height,
objectFit: mode, // cover, fill, contain
};
return <img ref={imgRef} src={loaded ? src : placeholder} style={{ ...props.style, ...imageStyle }} alt={alt} />;
};
Image에서 설정할 모든 값들을 props로 받고
Lazy의 여부와 옵션 값들 또한 props를 통해 전달함으로써 재사용성을 높였다.
Lazy Loading 적용하기
useEffect(() => {
if (!lazy) {
setLoaded(true);
return;
}
const handleLoadImage = () => setLoaded(true);
const imgElement = imgRef.current;
imgElement && imgElement.addEventListener(LOAD_IMG_EVENT_TYPE, handleLoadImage);
return () => {
imgElement && imgElement.removeEventListener(LOAD_IMG_EVENT_TYPE, handleLoadImage);
};
}, [lazy]);
useEffect(() => {
if (!lazy) return;
if (!observer) { // observer가 등록되어 있지 않다면 새로 생성
observer = new IntersectionObserver(onIntersection, { threshold });
}
// observer에 해당 컴포넌트의 image 등록
imgRef.current && observer.observe(imgRef.current);
}, [lazy, threshold]);
useEffect부분에서 이미지 엘리먼트에 커스텀 이벤트를 붙이는 부분과
observer를 통해 감시하는 부분으로 나누었다.
// 모듈내에서 전역적으로 사용되도록 바깥에 작성
let observer = null;
const LOAD_IMG_EVENT_TYPE = "loadImage";
const onIntersection = (entries, io) => {
console.log(entries);
entries.forEach((entry) => {
if (entry.isIntersecting) {
// 이 조건이 있어야 threshold 값대로 처리할 수 있음.
io.unobserve(entry.target);
entry.target.dispatchEvent(new CustomEvent(LOAD_IMG_EVENT_TYPE));
}
});
};
observer에 연결시킨 함수는 모듈 내에서 전역적으로 사용할 수 있도록
컴포넌트 바깥에서 작성했고, 뷰포트에 해당 이미지가 들어오면
해당 observer의 감시를 끄고 커스텀 이벤트를 실행시켰다.
intersection Observer는 무한스크롤을 구현할 때 몇번 사용해보기는 했지만
동작 원리에 대해서 아직 좀 미흡했었는데 이번 예제를 통해 조금은 더 이해가 되었다.
Spacer 컴포넌트
내부 요소들의 간격을 지정할 수 있는 컴포넌트.
<Spacer type="vertical">
<img src="https://picsum.photos/200" style={{ display: "block" }} />
<img src="https://picsum.photos/200" style={{ display: "block " }} />
</Spacer>
컴포넌트를 만들어서 type을 prop으로 전달하여 해당 type에 맞게 지정할 수 있다.
하위 엘리먼트 변환하기
const nodes = React.Children.toArray(children)
.filter((element) => React.isValidElement(element))
.map((element, index, elements) => {
return React.cloneElement(element, {
...element.props,
style: {
...element.props.style,
marginRight: type === "horizontal" && index !== elements.length - 1 ? size : undefined,
marginBottom: type === "vertical" && index !== elements.length - 1 ? size : undefined,
},
});
});
return (
<div {...props} style={spacerStyle}>
{nodes} // 새로 만든 자식들을 보여준다.
</div>
);
React.Children.toArray(children) 를 통해 하위 엘리먼트를 배열 형태로 받을 수 있으며
받은 요소들을 가공하여 새로운 엘리먼트를 반환하여 처리할 수 있다.
* 하위 엘리먼트가 아닌 "하위 컴포넌트"에도 적용하려면
아래와 같이 하위 컴포넌트에 props로 받은 style을 지정하도록 작성 해두어야 한다.
const Box = ({ width = 100, height = 100, ...props }) => {
const style = {
width,
height,
};
return <div {...props} style={{ ...props.style, ...style }} />;
};
export default Box;
Spinner 컴포넌트
svg파일과 props로 size를 전달해주어 간단하게 로딩 spinner를 구현할 수 있었다.
import styled from "@emotion/styled";
const Icon = styled.i`
display: inline-block,
vertical-align: middle
`;
const Spinner = ({ size = 24, color = "#919eab", loading = true, ...props }) => {
const sizeStyle = {
width: size,
height: size,
};
return loading ? (
<Icon>
<svg
version="1.1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 100"
style={sizeStyle}
>
<!----- svg 코드 작성 ------>
</svg>
</Icon>
) : null;
};
export default Spinner;
내가 실습 했을 때는 애니메이션이 들어가 있는 svg파일이었기 때문에
css 애니메이션 처리 없이 적용했는데, 이미지로 진행하려면 내부에서 css 애니메이션을 사용하면 될 듯 하다.
Toggle 컴포넌트
디자인이 들어간 toggle버튼의 경우 checked와 disabled값을 props로 받아서
input에 전달하고 해당 input을 숨기는 형태로 구현할 수 있다.
// ToggleContainer, ToggleInput, ToggleSwitch css 작성하기
const Toggle = ({ name, on = false, disabled = true, onChange, ...props }) => {
const [checked, toggle] = useToggle(on);
const handleChange = () => {
toggle();
onChange && onChange();
};
return (
<ToggleContainer {...props}>
<ToggleInput type="checkbox" name={name} checked={checked} disabled={disabled} onChange={handleChange} />
<ToggleSwitch />
</ToggleContainer>
);
};
원래의 나였다면 input이 아닌 state들을 추가해서 구현했었을텐데
이러한 트릭을 사용하여 구현하는 방식이 굉장히 흥미로웠다.
이전에 파일 불러오기 버튼을 input을 숨기고 따로 버튼을 추가해서 처리를 했던 사례를 본 적 있었는데
다양하게 응용할 수 있다는 것을 알게 되었다.
✍ 느낀 점
리액트를 하면서 이전에 다루었던 것들이 나오기는 했지만 새롭게 익히는 느낌이 들었다.
예를들어 이전에는 그냥 "이렇게 사용하는건가 보다~" 라고 생각했다면
요즘은 "이런 이점이 있어서 이렇게 사용하는것이구나~" 하는 깨달음과 함께 머릿속에 들어오는 기분이다.
그리고 응용하고 활용하는데에 있어서 나는 아직 많이 부족하다는 것을 느꼈다.
그런 면에 있어서 이번 강의들은 너무 너무 유익했다!
'스터디' 카테고리의 다른 글
프로그래머스 데브코스 TIL - Day 53 (0) | 2022.06.02 |
---|---|
프로그래머스 데브코스 TIL - Day 51 (0) | 2022.05.30 |
프로그래머스 데브코스 TIL - Day 49 (0) | 2022.05.26 |
프로그래머스 데브코스 TIL - Day 48 (0) | 2022.05.25 |
프로그래머스 데브코스 TIL - Day 43~47 복습기간 회고 (0) | 2022.05.25 |