오늘 배운 것
- 리액트 state를 특정 조건에 따라서 필터링하여 갱신하고, 또 삭제하여 갱신하는 연습을 했다!
새롭게 알게 된 것
- 오늘 내용은 Advanced 요구사항 구현을 빙자한 삽질이 대부분이라 어떻게 정리를 해야 할지 감이 안 온다. 일단 오늘의 요구사항부터 정리해보자!
- 과제의 주요 요구사항은 C:사용자 입력데이터로 트윗 생성, R:모든 트윗 불러오기, D:버튼 클릭 시 트윗 삭제이다. 여기에 추가로 select와 option을 활용해 선택한 사용자가 쓴 글만 보이도록 하는 드롭다운 메뉴가 필요하다.
드롭다운 메뉴 만들기
- 먼저 드롭다운 메뉴를 만들기 위해서.. 처음에는 컴포넌트로 생각하고 접근했다. 이 부분까지 컴포넌트로 따로 분리하여 작성하는 것이 맞는지 잘 모르겠는데, 분리해서 작업을 한 결과 과정이 꽤 복잡했기 때문에 이렇게 작은 부분까지 컴포넌트로 분리할 필요는 없겠다는 생각이 들었다. (물론 상황에 따라 다르겠지만...)
- 아래 코드가 그 삽질의 결과물이다.(즉 일부만 작동된다..) Tweets 페이지 컴포넌트에서 호출되는 Selection 컴포넌트인데, props로 전체 트윗 목록과, 트윗목록(state) 갱신 함수, 그리고 이전 트윗 목록의 상태를 전달받는다. 나머지 동작들은 괜찮았는데(?) 안 되는 부분은 목록에서 초기 옵션을 클릭 시 전체 목록을 다시 렌더링 하는 부분이었다.
- 목록에서 초기 옵션을 선택했을 때 전체 목록을 표시하기 위해서 직전 트윗 목록을 가져와서 리렌더링을 했었다. 문제는 한 번 바뀐 상태는 괜찮지만 두 번 바뀌었을 때는 직전의 state도 이미 바뀐 값이기 때문에 변경이 적용된다는 것이다. useEffect와 useRef를 활용해 직전 상태를 만들어준 것은 좋았지만, 그런 부분을 생각하지 못했다.
- 이 모든 문제가 기존 tweet 목록과 filtered 된 tweet 목록을 별도의 state로 구분했어야하는데 그러지 않았기 때문에 생긴 일인 것 같다. 렌더링 하면서 기존 tweet 목록을 변경하니까 이전 값이나 원래 목록으로 아무리 돌아가고 싶어도 돌아갈 곳이 없는 것이다 ㅜㅜ.
const Selection = props => {
const {tweets, renderTweets, prevTweets} = props;
const onChangeSelection = e => {
const val = e.target.value;
if (val !== 'false') {
const tweet = tweets.filter(ele => ele.username === e.target.value);
renderTweets(tweet);
} else {
renderTweets(prevTweets);
console.log(prevTweets);
}
};
const tweetsUsernames = [...new Set(tweets.map(tweet => tweet.username))];
return (
<select onChange={onChangeSelection}>
<option value={false}> --- 사용자 이름 선택 --- </option>
{tweetsUsernames.map((name, idx) => {
return (
<option value={name} key={idx}>
{name}
</option>
);
})}
</select>
);
};
// Tweets 컴포넌트 내부의 일부 코드이다. 이전 상태 기억하는 부분
const prevTweets = usePrevious(tweets);
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
//...
//...상위 페이지 컴포넌트인 Tweets 내부에서 불러온다.
{/* select */}
<div className="tweet__selectUser">
<Selection renderTweets={renderTweets} tweets={tweets} prevTweets={prevTweets} />
</div>
- 코드가 길지만 어차피 작동하지 않기 때문에.. 삽질했던 시간이 아까워서라도 기록으로 남겨본다. 깊게 공부하진 못했지만 useEffect와 useRef 에 대해서 대략적으로나마 알고 활용해 볼 수 있어서 좋은 경험이었다.
- 컴포넌트로 별도로 분리하니, 상태 업데이트 함수도 props로 전달해주어야하고, 바뀐 상태를 적용하는 게 조금 복잡하다는 생각이 들어서, Tweets 페이지 컴포넌트 내에서 직접 입력하고 상태를 변경하는 방식으로 코드를 다시 작성했다.
- 한 가지 배운 점은, 사용자가 드롭다운 메뉴에서 이름을 선택했느냐 선택하지 않았느냐, 즉 필터링을 해서 보여주어야 되느냐 그렇지 않느냐도 state가 될 수 있다는 것이다. 처음에는 선택한 이름을 state로 만들어서 그것을 기준으로 필터링한 새 배열을 만들어서 뿌려주었는데, 이런 접근도 가능하다는 것을 새롭게 알게 되었다. 아래는 코드의 일부.
// Tweets 컴포넌트 내부에 정의된 필터링 함수
const handleFilter = e => {
if (e.target.value === 'All') {
setTweets(tweets);
setIsFiltered(false);
} else {
const filtered = tweets.filter(tweet => tweet.username === e.target.value);
setFilteredTweets(filtered);
setIsFiltered(true);
}
};
return (
<select onChange={handleFilter}>
<option value="All"> --- 사용자 이름 선택 --- </option>
{tweetsUsernames.map((name, idx) => {
return (
<option value={name} key={idx}>
{name}
</option>
);
})}
</select>
// ---
<ul className="tweets">
{isFiltered
? filteredTweets.map(tweet => {
return <Tweet tweet={tweet} key={tweet.id} handleDelete={handleDelete} />;
})
: tweets.map(tweet => {
return <Tweet tweet={tweet} key={tweet.id} handleDelete={handleDelete} />;
})}
</ul>
);
- 추가적으로 트윗 삭제 기능은 아직 구현을 못했다 ㅜ. 레퍼런스가 있긴 하지만 조금 복잡하기도 하고, 다른 예제를 활용해서 만들면서 연습하려고 한다.
SUMMARY
잘한 점
- 오전에 알고리즘 문제 풀고, 타입스크립트 공부하고, 블로깅 한 것!
보완할 점
- 지금까지 배운 리액트 state, props 개념을 활용해서 간단한 todo 앱을 만들면서 복습하면 좋을 것 같다! 이번 주 주말까지 만들고 결과물 블로그에 업로드해보기.
느낀 점
- 오늘 어려웠던 점은 어떤 부분을 state로 등록해야할지 감이 잘 오지 않는 것이었다. 변하는 모든 부분이 state는 아닐 것이고, 또 어떤 관점으로 state를 바라보느냐에 따라서 코드의 방향이 바뀐다고 생각하니까 좀 더 신중하게 생각하고 접근해야겠다고 느꼈다.
- 페어님이 앞부분 챌린지를 미리 끝내고 오셔서 오늘은 거의 따로 진행을 했다. 나도 지금까진 과제를 미리 해오는 부분에 있어서 별 생각이 없었던 것 같은데, 그래도 같이 하는 것에 의미가 있다고 있는 것 같다. 앞으로는 페어를 좀 더 배려하려고 노력해야 되겠다는 생각이 들었다...!
- 재미가 있으면서도, 더 많이 빨리 배우려면 이것을 왜 이렇게 써야되는지 '왜????' 라는 의문을 늘 품고 공부해야 하는 것 같다.
'TIL' 카테고리의 다른 글
[Day 32] 2022-0805 (0) | 2022.08.08 |
---|---|
[Day 31] 2022-0804 (0) | 2022.08.05 |
[Day 29] 2022-0802 : props, state 활용해 SPA 만들기 (0) | 2022.08.03 |
[Day 28] 2022-0801 : SPA와 리액트 라우터/ useNavigate (0) | 2022.08.01 |
[Day 26] 2022-0728 : fetch API 다양한 방법으로 활용하기 (0) | 2022.07.29 |