코드를 짜다보니, 댓글과 대댓글 기능을 구현할 일이 생기게 되었다.
그런데 버그가 하나 생겼다.
댓글을 입력하면 잘 올라가는데 문제는
댓글입력을 버튼을 하나만 눌렀는데, 전체 댓글창이 모두 열리게 되었다.
동시 다발적으로 모두 열리게 되는 사고가 생긴 것이다. 어떻게 하면 좋을까 고민을 했다. state를 새로 만들어주는게 맞을까? 아니면 기존에 받아오는 state를 이용해서 if문이나 삼항연산자를 써서 사용하는게 맞으려나 매우 고민이 되기 시작했다.
어떻게 접근하면 좋을까?
spread 연산자([...replyBtn])을 사용해볼까? 테스트를 해보았더니 그렇게 되지 않았다. 바로 지웠다. 그런데 내가 배운 로직으로는 그렇게 쓰는게 맞을 텐데 그러면 도대체 뭐가 빠졌던 것이였을까
map 함수를 쓸 때
map ((element, index) => ()) 가 생각이 났다.
아~ 이때 index를 통해서 댓글을 구분해주는 것도 괜찮겠다 싶어서 거기에서 실마리를 풀어보려고 하였다.
그러면 무엇을 구분해 주어야할까?
일단 댓글 버튼을 누를때는 index번호가 있어야지 어떤 댓글버튼인지 구분이 갈 것 같았다. 그래서 일단 한번 해보자
<ReplyButton onClick={() =>
{
let copy = [...replyBtn] //replybtn state에 복사본을 만들어준다
copy[i] = !copy[i] // 각 댓글마다 답글 버튼의 상태를 저장하기
setReplyBtn(copy) // setState에 copy를 넣어서 수정해준다
}
}>댓글
</ReplyButton>
copy[i] = !copy[i]를 한 이유는
i번째 댓글에 대한 답글 버튼 상태를 현재와 반대로 변경하여 저장하는 역할을 하고 있는 것이다.
이상태에서 테스트를 해보았다. 전혀 변화가 없었다. 도리어 오류가 발생했다고 나왔는데,
이때
밑에 보이는
{replyBtn && ... }가 보이는 것이었다
여기도 마찬가지로 구분해주기 위해서는
index를 넣어줘야한다.
{replyBtn[i] &&
...
}
그렇게 바꿔주고 또 실행을 해보았다.
오류가 또 나네 ;;
하 iterable은 무슨소리야
규칙이 없다?? 그 얘기를 들으니 전체를 아우르고 있는, state가 문제가 있을 수 있겠다는 생각이 들었다.
state를 나는 false로 처리해두고 있었다.
그렇다면 모든 state가 동시다발적으로 false가 되니까 배열로 담아서 만들어보면 되지 않을까?
comments라는 state는 배열로 선언해놓은 상태인데,
comments = useState([]) 이것의 길이 만큼, replyBtn 만큼 생성되어야하고, state안에 값을 형변환 해주면 되겠다라는 생각이 들었다.
const [replyBtn, setReplyBtn] = useState(Array(comments.length).fill(false)) //comments의 길이 만큼 배열로 만들고, false를 채워줘라!
그렇게 했더니!!! 놀랍게도 댓글 버튼을 눌러도 독립적으로 모달창이 열렸다!
기존코드
const StudyDetailPage = () => {
const [textWrite, setTextWrite] = useState('')
const [comments, setComments] = useState([])
const [replyBtn, setReplyBtn] = useState(false)
return(
{comments?.map((el, i) => (
<StudyDetailCommentWrap2>
<div>
<StudyProfileNameWrap2>
<ProfileFlexWrapper>
<StudyProfileIcon src={ProfileIcon} />
<StudyWriterWrapper>
<span>글쓴이</span>
<span>8:20</span>
</StudyWriterWrapper>
</ProfileFlexWrapper>
</StudyProfileNameWrap2>
<TextReplyWrapper>{el}</TextReplyWrapper>
<ReplyButton onClick={() =>
setReplyBtn(!replyBtn)}>
댓글
</ReplyButton>
{replyBtn &&
<ReplyWrapper>
<StudyTextContentsWrapper3>
<StudyProfileTextWapper>
<StudyReplyIconStyle src={ProfileIcon} />
<StudyProfileNameWrap>
<span>닉네임</span>
</StudyProfileNameWrap>
</StudyProfileTextWapper>
<StudyTextAreaWrapper>
<TextField
onChange={(e) => {
setTextWrite(e.target.value)
}}
id="standard-textarea"
placeholder="댓글을 작성해주세요"
multiline
variant="standard"
style={{ width: "1000px" }}
/>
</StudyTextAreaWrapper>
<StudySubmitWrapper>
<CheckboxWrapper>
<Checkbox
{...label}
defaultChecked
sx={{ '& .MuiSvgIcon-root': { fontSize: 28 } }}
/>
<span>비밀댓글</span>
</CheckboxWrapper>
<div>
</div>
<StudySubmitButton onClick={() => {
setComments([...comments, textWrite]);
setTextWrite('');
}}>
댓글작성
</StudySubmitButton>
</StudySubmitWrapper>
</StudyTextContentsWrapper3>
</ReplyWrapper>
}
</div>
</StudyDetailCommentWrap2>
))}
)
}
변화된 코드
const StudyDetailPage = () => {
const [recruitType, setRecruitType] = useState(true)
const [textWrite, setTextWrite] = useState('')
const [comments, setComments] = useState([])
const [replyBtn, setReplyBtn] = useState(Array(comments.length).fill(false));
return(
{comments?.map((el, i) => (
<StudyDetailCommentWrap2 key={i}>
<div>
<StudyProfileNameWrap2>
<ProfileFlexWrapper>
<StudyProfileIcon src={ProfileIcon} />
<StudyWriterWrapper>
<span>글쓴이</span>
<span>8:20</span>
</StudyWriterWrapper>
</ProfileFlexWrapper>
</StudyProfileNameWrap2>
<TextReplyWrapper>{el}</TextReplyWrapper>
<ReplyButton onClick={() =>
{
let copy = [...replyBtn] //replybtn state에 복사본을 만들어준다
copy[i] = !copy[i] // 각 댓글마다 답글 버튼의 상태를 저장하기
setReplyBtn(copy) // setState에 copy를 넣어서 수정해준다
}
}>댓글</ReplyButton>
{replyBtn[i] &&
<ReplyWrapper>
<StudyTextContentsWrapper3 >
<StudyProfileTextWapper>
<StudyReplyIconStyle src={ProfileIcon} />
<StudyProfileNameWrap>
<span>닉네임</span>
</StudyProfileNameWrap>
</StudyProfileTextWapper>
<StudyTextAreaWrapper>
<TextField
onChange={(e) => {
setTextWrite(e.target.value)
}}
id="standard-textarea"
placeholder="댓글을 작성해주세요"
multiline
variant="standard"
style={{ width: "1000px" }}
/>
</StudyTextAreaWrapper>
<StudySubmitWrapper>
<CheckboxWrapper>
<Checkbox
{...label}
defaultChecked
sx={{ '& .MuiSvgIcon-root': { fontSize: 28 } }}
/>
<span>비밀댓글</span>
</CheckboxWrapper>
<div>
</div>
<StudySubmitButton onClick={() => {
setComments([...comments, textWrite]);
setTextWrite('');
}}>
댓글작성
</StudySubmitButton>
</StudySubmitWrapper>
</StudyTextContentsWrapper3>
</ReplyWrapper>
}
)
}
export default StudyDetailPage