App.js로 상위 컴포넌트를 작성했으니 이제 하위 컴포넌트를 작성할 것이다.
(아직 하위 컴포넌트의 메소드를 작성 안했다. 하위 컴포넌트를 만들면서 하나씩 추가할 것이다.)
//함수 컴포넌트 형식, props를 사용하고 하위 컴포넌트의 props에 props를 전달할 수 있다.
//상태(state)를 가지지 못하여 상위 class 컴포넌트에서 상태를 props로 받아, 상위 state를 변경할 수 있다.
//{ }안에 App.js에서 받은 props 값, 메소드가 있다.
//구조분해화라고 하며 props 객체 안의 props(속성들)를 좀 더 쉽게 작성할 수 있다.
//매개변수를 props로 받을 경우, props객체의 속성을 props.속성이름 형식으로 써야 한다.
const Nav =({handleSearchInputChange,
handleSettingButtonClick,
user,
darkMode
}) => {
return (
//컴포넌트 태그만 작성되기 때문에 {}는 없어도 된다.
//하지만 console.log등을 사용하기 위해선 {}가 필요하기 때문에 작성하였다. {}를 쓸때는 return ()으로 작성해야 한다.
<nav className={darkMode ? "btn btn-dart" : btn btn-light}>
<div className="col-md-6 col-md-offset-3">
<Search
handleSearchInputChange={handleSearchInputChange}
darkMode={darkMode}
/>
</div>
<span>{user ? user.name : ""}님이 로그인했습니다.</span>
<button
className={darkMode? "btn btn-dark" : "btn btn-light"}
onClick={handleSettingButtonClick}
>setting
</button>
</nav>
)
};
//export name: name으로 특정한 모듈을 export한다.
//한 파일내 여러 변수, 클래스, 함수를 export할 수 있다.
//export default name: 파일 내에서 단 하나의 변수, 클래스 등을 export한다.
//var, let, const가 붙은 변수르 바로 default할 수는 없다(export default const Nav -> x)
export default Nav;
//App.js
getYouTubeVideos(query){
let options ={
key: YOUTUBE_API_KEY,
query: query,
max: 2
}
serarchYoutube(options, videos =>{
//setStaet()로 상위 클래스의 state 상태를 변경할 수 있다.
this.setState({
videos: videos, //youtueAPI에서 받아온 비디오 정보 객체를 담은 배열
currentVideo: videos[0] //0번째 배열의 영상정보(재생가능)
})
);
}
videos의 구조와 server 호출 정보는 다른 포스트에서 다룰 것이다.
{handleSettingButtonClick}은 App.js에서 handleSettingButtonClick()을 받은 props이다. setting 버튼을 클릭하여 state.isSettingOpen 값을 false, true로 변경해준다.
//App.js
handleSettingButtonClick(){
this.setState(prevState => ({
isSettingOpen: !prevState.isSettingOpen
}));
}
//Setting.js
class Setting extends React.Component {
constructor(props){
super(props);
this.state ={
username: props.user ? props.user.name : ""
};
}
//Setting 관련 메소드
//render 내용을 설명하면서 작성할 것이다.
render() {
return (
//isOpen에 따라 className이 변경된다.
//"mdl show"이면 visibility: visible css로 화면에 나타나고,
//"mdl"이라면 visibility hidden, 위치값 모두 0, position: apsolute로 화면에 나타나는 모습이 없다.
<div className={this.props.isOpen ? "mdl show" : "mdl"}>
//handleClose는 App.js에서 handleSettingButtonClick()을 가지고 있다. isSettingOpen을 변경해준다.
//"mdl-mask"는 mdl show 하위 태그인데 css가 absolute로 전체 화면을 차지하고 있다(약간 어두운 색) 이 화면을 클릭 시 handleClose가 실행되는 것이다.
<div className="mdl-mask" onClick={this.props.handleClose}></div>
<div className="sidebar">
<h3>Setting</h3>
<hr />
<fieldset>
<legend>사용자 설정</legend>
<span className="small-margin-right">사용자 이름</span>
<input
type="text"
className="small-margin-right"
value={this.state.username}
//Setting에서 handleChangeName(event)를 실행시킨다
onChange={this.handleChangeName.bind(this)}
/>
<button onClick={this.handleSave.bind(this)}>저장</button>
</fieldset>
<hr />
<fieldset>
<legend>일반</legend>
<label>
<input
type="checkbox"
className="small-margin-right"
onChange={this.handleToggleDarkMode.bind(this)}
></input>
<span>Dark Mode</span>
</label>
</fieldset>
</div>
</div>
);
}
}
handleChangeName(event){
this.setState({
//event는 이 메소드를 실행시킨 이벤트를 말한다. 여기서는 onChange가 된다.
//target은 event가 발생한 태그, value는 그 태그가 지닌 값이다.
username: event.target.value
});
}
//Setting.js
handleSave() {
//Setting이 받은 props는 App.js에서 넘어온 props이다.
//그러니 아래 메소드는 App.js의 handleUpdateSetting이 실행되는 것이다.
this.props.handleUpdateSetting("currentUser", {
name: this.state.username
});
}
//App.js
handleUpdateSetting(key, value) {
const pair = {};
pair[key] = value; //pair["currentUser"] = this.state.username(Setting.js의 username이다.)
this.setState(pair); //const pair ={currentUser: this.state.username(Setting.js) }
}
handleUpdateSetting은 위에서 handleSave()에서도 사용했다. 상태변경 메소드를 재활용 한다는 점에서 좋은 코드라고 생각한다.
//Setting.js
handleToggleDarkMode(event) {
this.props.handleUpdateSetting("darkMode", event.target.checked);
}
//App.js
handleUpdateSetting(key, value){
const pair ={};
pair[key] = value;//pair["darkMode"] = event.target.checked(true or false);
this.setState(pair);// pair = {darkMode: true or false}
}