거두절미하고 오늘의 주제는 무엇이냐.
바보가 아니라면 선택한 버튼만 초록색 border가 들어가는 css가 적용되어야 한다는 것을 알 것이다...
지금 짠 코드는 엄청 간단함.
const [selectDeco, setSelectDeco] = useState(false);
const handleClick = () => {
setSelectDeco(true);
}
return (
<div className={selectDeco ? "item_box_click" : "item_box"} onClick={() => handleClick()}>
{content}
</div>
)
사실 저렇게 돌아갈 걸 알고 있었다. 그냥 css 적용 확인하려고 짜본거임..
- 버튼 개수만큼 인자를 가진 array 생성. bool값이 인자로 들어갈 것임.
- bool = false로 초기화
- 버튼 선택 시 해당 인자만 true로 바꾸기.
- true인 버튼만 css 적용
- 버튼을 클릭할 때마다 2~4번 반복
짜보자.......
..나 이거 이렇게 오래 걸릴 줄 몰랐다.
결국 해내긴 햇는데 뭐임?
왜냐면.. 내 js 구조가 복잡하기 때문이다.
나는 기능/박스 별로 js를 분리하는 걸 참 좋아하는데 (한 곳에 코드 몰려있는 거 안 좋아함) 덕분에....... 또다시 부모 <-> 자식을 참조해야하는 일이 생겼던 것이다.
나는 item.js와 itembox.js를 갖고 있다.
Item.js에서 map으로 ItemBox를 반복문 생성해주는 구조임.
{ // 아이템 썸네일 박스
imgArray.map(
(imgSrc, index) => {
return (<Itembox type={typeItemState_cloth}
imgSrc={imgSrc}
index={index}/>)
}
)
}
imgArray
에는 서버에서 받아오는 이미지 url이 있다. (아직 서버 연결 안했다만.. 언제하지?)
itembox.js에서는 아래와 같은 코드를 return한다.
return (
<div className={btnState ? "item_box_click" : "item_box"} onClick={() => props.handleClick(index)}>
{content}
</div>
)
..간단하죠?
문제는 여기서 발생한다. 나는 itembox에 모든 버튼의 상태값을 담은 array를 생성한 뒤, 이걸 검사하며 itembox의 클래스네임을 설정해줄 생각이었는데 itembox가 각각의 js로 생성되며 공통된 변수를 가질 수 없게 된 것이다!!!!!!
아놔......
내 초반 생각은 이랬다.
- 버튼의 상태를 담은 array(이하
selectDeco
)는 item.js에 만들자.- item.js에 click() 함수를 두고, itemBox에서 버튼을 누르면 click()을 호출하자.
- click()에서
selectDeco
의 상태를 업데이트 해주자.- itemBox.js에
selectDeco
의 상태를 return 하자.
=> 함수 return이 안 돼서 실패.
자식에서 부모의 함수를 호출하는 건 되는데 그 함수의 반환값을 다시 자식으로 받는 건 안 되더라...
하.........................................
공통 변수 두는 무슨 라이브러리 있는 거 같았는데 고작 이런 기능으로 그렇게까지(?) 하고 싶지 않았음.
useEffect
를 써볼까?selectDeco
가 변하면 < Itembox selectDeco = {selectDeco}/> 이렇게 이 값만 보내는거지!
실패함. 감지를 못 하던데?
디버거까지 써서 해봤는데 보내도 itemBox.js에서 변수가 변경되지 않았다. 분명 useEffect는 실행됐는데.
마찬가지로 itemBox.js에도 useEffect를 두고 해당 변수가 변했을 경우 console.log()를 찍어봐도 나오지 않았다. 그냥 해당 useEffect를 타지 않았다.
그냥 통째로 컴포넌트를 재로드 하자!!!
아래는 부모. Item.js
//Item.js 부모
const [selectDeco, setSelectDeco] = useState(false);
const handleClick = (idx) => {
const newArr = Array(imgArray.length).fill(false);
newArr[idx] = true;
setSelectDeco(newArr);
}
const handleChangeContent = () => {
<div className="itemBoxDiv">
{
// 아이템 썸네일 박스
imgArray.map((imgSrc, index) => {
return (
<Itembox type={typeItemState_face}
imgSrc={imgSrc}
index={index}
handleClick={handleClick}
selectDeco={selectDeco}/>
)
})
} </div>
);
useEffect(() => {
handleChangeContent();
}, [selectDeco]);
selectDeco가 변할 때마다 useEffect로 컴포넌트 전체를 재로드했다.
재로드하면서 업데이트 한 selectDeco={selectDeco} 도 다시 보내줌.
아래는 자식. Itembox.js
useEffect(() => {
if (selectDeco[index]) { // 해당 버튼이 클릭됐으면
setBtnState(true);
} else {
setBtnState(false);
}
}, [selectDeco]);
const [btnState, setBtnState] = useState(false);
return (<div className={
btnState
? "item_box_click"
: "item_box"
}
onClick={
() => props.handleClick(index)
}> {content} </div>)
업데이트 한 selectDeco가 들어오면 useEffect에서 감지한다.
그래서 selectDeco의 index에 해당하는 인자가 true면 버튼 css를 적용하고, 아니면 해제한다.
후.........
이거 진짜 쌩뇌로 짰다.
재랜더링하는 게 메모리 많이 잡아먹을까봐 걱정해서 최대한 피한건데 결국 재랜더링하는 게 좀 마음에 걸리기도 하고.. 근데 저 쪼끔 재랜더링한다고 뭐 얼마나 과부화 되겟어? 저게 제일 효율적일수도; (쓰레기 마인드)
2달 해외살이 후 토요일에 귀국해서 월화수목 밤을 샜더니 정신이... 어우 어지러워
그래도 뿌듯..하다......웅....