2차 프로젝트 때의 기억을 더듬어 써보는 포스팅.. 코드 엉성함 주의 😂😂😂
sibling component간 데이터 교환
일반적으로 우리가 부모 component에서 자식 component로 데이터를 보낼땐 props를 이용한다.
하지만, 반대로 자식에서 부모에게 데이터를 보낼 수 있는 것일까?!
정통(?)적으론 힘든 일이라서 리덕스나 컨텍스트같은 개념들이 나왔겠지..?
하지만 우리의 리액트세상에 불가능이란 없다!! 할 수는 있다 ㅋㅋ
콜백함수
그때도 지금도 나에겐 아직 넘나 어색한 콜백함수를 이용하면 자식에서 부모로 정보를 넘길 수 있다.
그렇다면 콜백함수란 뭘까?
바로바로!! 함수를 인자로 사용하는 함수를 말한다.
예시를 통해 살펴보자…
2차 프로젝트인 애드워드 프로젝트에서 골치 아팠던 것 중 하나..
동영상 업로드 버튼을 누르면 모달창이 뜨는데!
(막판에 급하게 모바일 버전으로 수정하느라 css가 깨진건 무시해주세요 ㅋㅋ)
왼쪽에 광고영상 업로드, 퀴즈 업로드라는 두개의 탭이 있고, 각각의 탭을 누를 경우 오른쪽 빈 공간에 해당 탭의 컴포넌트를 불러오는 식이다.
근데 우리 프로젝트 특성상, 광고영상 고유의 id가 있어야 했고, 그 아이디는 광고정보를 ‘POST’로 보냈을 때 모든 정보가 유효하면 백앤드쪽에서 넘어 오는 id였고, 나는 해당하는 id를 퀴즈 업로드 시 ‘POST’로 같이 부쳐줘야 했다!
이틀을 고민해도 답이 안 나왔는데.. 동기 사랑~ 나라 사랑~~ 광훈님의 도움으로😆 콜백함수를 이용하게 되었다!
일단.. 코드를 보기 전 내 컴포넌트 구조를 다시 한번 살펴보자면!
ModalForm이라는 부모 컴포넌트 하에
AdVideoForm과 AdQuizForm이라는 자식 컴포넌트들이 있는 건데..
resId라는 값을 자식=>부모=>자식 컴포넌트 순으로 전달을 해야 하는 것!
일단 부모 컴포넌트에 콜백함수를 작성한다.
// ModalForm.js(부모 컴포넌트)
class ModalForm extends Component {
constructor() {
super();
this.state = {
activeTab: "videoTab",
visible: true,
resId: 0
};
}
onUpdateResId = resId => {
this.setState({
resId: resId
});
};
switchMenu = activeTab => {
this.setState({ activeTab: activeTab });
};
onClose = () => {
const { visible } = this.state;
if (visible === !!visible) {
this.setState({
visible: false
});
} else {
this.setState({
visible: true
});
}
};
render() {
let subView = [
{ videoTab: <AdVideoForm onUpdateResId={this.onUpdateResId} key="0" /> },
{ quizTab: <AdQuizForm key="1" resId={this.state.resId} /> }
];
return (
<ModalBackground canSee={this.visible}>
<ModalWrapper>
<TitleWrapper>
<TitleLetters>Adwards</TitleLetters>
</TitleWrapper>
<AdVideoWrapper>
<AdLeft>
<LeftUL>
<AdSideLILef
active={this.state.activeTab === "videoTab"}
onClick={() => this.switchMenu("videoTab")}
>
<Span>광고 영상 업로드</Span>
</AdSideLILef>
<AdSideLIRig
active={this.state.activeTab === "quizTab"}
onClick={() => this.switchMenu("quizTab")}
>
<Span>퀴즈 업로드</Span>
</AdSideLIRig>
</LeftUL>
</AdLeft>
<AdRight>
{subView.map((el, key) => {
el.key = key;
return el[this.state.activeTab];
})}
</AdRight>
</AdVideoWrapper>
<CloseOK>
<CloseLetter cancel onClick={this.onClose}>
취소
</CloseLetter>
<Link to="/biz_main" style={{ textDecoration: "none" }}>
<CloseLetter>확인</CloseLetter>
</Link>
</CloseOK>
</ModalWrapper>
</ModalBackground>
);
}
}
하나하나 코드를 다시 뜯어보자면
먼저 부모 컴포넌트의 state에 resId를 0으로 초기화 시켜주고..
onUpdateResId 라는 함수를 만들고, 들어오는 값과 동일한 값으로 state값을 업데이트 시키기
이제 자식컴포넌트로 가서…
// AdVideoForm(resId를 받는 자식컴포넌트)
// .... 중략..
onClickSave = e => {
const {
videoTitle,
videoLink,
videoThumbnail,
videoCate,
videoDesc,
videoBudget,
videoPrice,
tags,
videoInterests
} = this.state;
const title = videoTitle.trim();
const link = videoLink.trim();
const thumbnail = videoThumbnail.trim();
const desc = videoDesc.trim();
const cate = videoCate;
const budget = videoBudget.trim();
const price = videoPrice.trim();
const interests = videoInterests;
const tag = tags;
if (
title === "" ||
link === "" ||
thumbnail === "" ||
desc === "" ||
cate === "" ||
budget === "" ||
price === ""
) {
alert("모든 칸을 채워주세요");
} else {
fetch("http://13.125.254.18:8000/advertisement", {
method: "POST",
headers: {
Authorization: `${AuthAd}`
},
body: JSON.stringify({
title: title,
video_link: link,
thumbnail: thumbnail,
ad_category_id: cate,
description: desc,
budget: budget,
price_per_view: price,
tag: tag,
interests_type_id: interests
})
})
.then(response => {
if (response.status !== 200) {
return response.json();
} else {
alert("영상 정보가 저장되었습니다.");
return response.json();
}
})
.then(response => {
this.props.onUpdateResId(response);
this.setState({
resId: response
});
});
// 중략...
다시 코드를 하나 하나 뜯어보자! 씹고 뜯고 맛보고 즐기고~😜
아까와 마찬가지로 resId를 state안에 넣어서 초기화 해주고..
유저가 입력한 정보가 서버로 잘 보내지면 response에서 resId가 넘어오는데 여기서 부모컴포넌트에 썼던 onUpdateResId라는 함수를 props로 넘겨서 response를 받는다.
개발자도구-네트워크 탭을 확인해보면 값이 아주 잘 들어왔다!
이렇게 resId를 업데이트 해주고 다시 부모컴포넌트로 돌아가서..
response를 통해 받은 값을 콜백함수를 통해 resId에 담아서 업데이트를 해주었다!
이제 이 업데이트한 값을 또 다른 자식 컴포넌트로 보내야 하는데..
바로 아래에 또다른 자식컴포넌트를 부르는 탭이 있어서
저기다가 바로 resId를 부모 컴포넌트의 state의 resId라고 대입을 해준다!
이제 AdQuizForm 컴포넌트로 넘어가보자.
onSaveQuiz = e => {
const {
quizQuestion,
quizRightAnswer,
testOne,
testTwo,
testThree,
testFour
} = this.state;
const question = quizQuestion.trim();
question.trim();
if (
question === "" ||
testOne === "" ||
testTwo === "" ||
testThree === "" ||
testFour === ""
) {
alert("모든 칸을 채워주세요");
} else {
fetch("http://13.125.254.18:8000/quiz", {
method: "POST",
headers: {
Authorization: `${AuthAd}`
},
body: JSON.stringify({
ad_id: this.props.resId.advertisement_id,
quizzes: [
{
content: quizQuestion,
choices: [testOne, testTwo, testThree, testFour],
answers: quizRightAnswer
}
]
})
}).then(response => {
if (response.status !== 200) {
return response.json();
} else {
alert("퀴즈 정보가 저장되었습니다.");
}
});
}
};
자식=>부모로 넘기는게 진짜 힘든 과정이었는데 이제 정말 다 왔다!
부모=>자식으로 넘기는건 걍 props 쓰면 되니까 넘나 간단한 것~
확인차.. render 밑에다 console.log(this.props, ‘props)를 찍어보았다.
아주 잘 찍히고 있다!
서버로 보낼때는 adid라는 이름으로 보내야 하기 때문에
Body에 `adid: this.props.resId.advertisement_id`라는 값을 실어준다.
그럼 해결쓰!
이걸 해결하고 나서의 교훈..
- 구조는 최대한 간단하게 짜자. depth가 깊어지면 쥬금…ㅠㅠ
- 리덕스를 공부하자.
애초에 리덕스를 공부했으면 이걸로 고민했을 일이 없는 거였는데! 리덕스 안 쓰고 쌩으로 하려니 넘나 힘들었던 것… 기술은 괜히 발전하는게 아니다 ㅋㅋㅋㅋ