자식 컴포넌트에서 변경된 State를 부모 컴포넌트에게 props 형태로 보내줄 수 있습니다.
const MovieForm = ({ addMovie }) => {
const [movieTitle, setMovieTitle] = useState('');
const [movieYear, setMovieYear] = useState('');
const onSubmit = (event) => {
// refresh 막기 위해 사용
event.preventDefault();
addMovie({
title: movieTitle,
year: movieYear,
});
};
return (
<form onSubmit={onSubmit}>
<input
type="text"
value={movieTitle}
placeholder="영화제목"
onChange={e => setMovieTitle(e.target.value)}
/><br />
<input
type="text"
value={movieYear}
placeholder="개봉년도"
onChange={e => setMovieYear(e.target.value)}
/><br />
<button type="submit">영화추가</button>
</form>
);
};
⇒ 인자로 전달받을 때, 사용하고자 하는 인자만 props형태를 사용하지 않고도 객체 형태로 받아와 사용할 수 있다. - { addMovie }
. 사용하고자 하는 데이터의 종류가 적다면 유용합니다.
→ 사용할 때 props.addMovie
로 사용하지 않고 바로 addMovie
로 사용가능합니다.
자식 컴포넌트에서 영화 제목과 개봉 년도의 state를 변경하려고 할 때, addMovie() 함수에서 변경된 state를 부모 컴포넌트에 전달해주어야합니다.
const addMovie = (movie) => {
setMovies([
...movies,
movie
]);
};
return (
<div className="App">
<h1>Movie list</h1>
<MovieForm addMovie={addMovie}/>
{renderMovies}
</div>
);
<MovieForm addMovie={addMovie}/>
movie
를 받아와 영화 리스트에 추가해줍니다.영화를 추가할 때 아무 정보도 입력하지 않으면 영화 제목과 개봉년도가 나오지 않고 0으로 정보가 입력됩니다. 유효한 영화만을 추가하도록 코드를 작성하도록 합니다.
const [titleError, setTitleError] = useState('');
const [yearError, setYearError] = useState('');
const validateForm = () => {
let validated = true;
// 영화 제목과 개봉 년도가 들어있지 않으면 실행
if (!movieTitle) {
setTitleError('영화제목을 입력해주세요');
validated = false;
}
if (!movieYear) {
setYearError('개봉년도를 입력해주세요');
validated = false;
}
return validated;
};
validated
는 영화가 유효한지 참, 거짓 여부를 알려주는 boolean 변수입니다. movieTitle
이 없거나 movieYear
값이 없으면 에러 메세지를 useState를 이용해 변경해주고 validated
를 false로 변경해줍니다.
const onSubmit = (event) => {
// refresh 막기 위해 사용
event.preventDefault();
if (validateForm()) {
addMovie({
// 식별 가능한 id 추가
id: Date.now(),
title: movieTitle,
year: movieYear,
});
}
};
영화 정보가 유효한지 확인 validateForm()의 리턴값으로 확인하고, 유효하면 addMovie() 함수로 영화 정보를 추가해줍니다.
영화 제목 또는 개봉년도를 입력하지 않았을 때 제대로 기입하라는 에러문구를 띄워줍니다.
return (
<form onSubmit={onSubmit}>
<input
type="text"
value={movieTitle}
placeholder="영화제목"
onChange={e => setMovieTitle(e.target.value)}
/><br />
<div style={{color: 'red'}}>{titleError}</div>
<input
type="number"
value={movieYear}
placeholder="개봉년도"
onChange={e => setMovieYear(e.target.value)}
/><br />
<div style={{color: 'red'}}>{yearError}</div>
<button type="submit">영화추가</button>
</form>
);
입력창 아래에 각각의 에러문구가 뜨게 됩니다. 유효한 경우에는 에러 문구가 빈 문자열이기 때문에 화면상에 아무런 에러문구가 뜨지 않습니다. 하지만, 유효하지 않은 경우에는 각각 titleError와 yearError가 화면상에 나타납니다.
⇒ style
을 사용해 에러 문구 색상을 빨간색으로 변경해줍니다.
const onSubmit = (event) => {
// refresh 막기 위해 사용
event.preventDefault();
if (validateForm()) {
addMovie({
// 식별 가능한 id 추가
id: Date.now(),
title: movieTitle,
year: movieYear,
});
resetErrors();
resetForm();
}
};
유효한 영화 정보를 입력하고 정보를 추가했으면, resetErrors() 함수로 에러문구를 빈 문자열로 변경해주고, resetForm() 함수로 입력창을 비워줍니다.
<input
type="text"
value={movieTitle}
placeholder="영화제목"
onChange={e => setMovieTitle(e.target.value)}
/><br />
<div style={{color: 'red'}}>{titleError}</div>
input 형식이 반복되기 때문에 컴포넌트로 변경합니다.
const InputField = ({
type,
value,
placeholder,
onChange,
errorMessage
}) => {
return (
<div>
<input
type={type}
value={value}
placeholder={placeholder}
onChange={onChange}
/><br />
<div style={{color: 'red'}}>{errorMessage}</div>
</div>
);
};
props로 type
, value
, placeholder
, onChange
, errorMessage
데이터를 부모 컴포넌트로부터 받아옵니다.
주의!
컴포넌트를 만들 때 root element로 감싸주어야 에러가 뜨지 않기 때문에 <div>태그
로 <input>
을 감싸줍니다. (무조건 div가 아니어도 됩니다.)
컴포넌트를 사용할 때 루트 element에 <div>
태그를 표시하는 것이 싫다면 <React.Fragment>를 사용해줍니다.
<React.Fragment>
<input
type={type}
value={value}
placeholder={placeholder}
onChange={onChange}
/><br />
<div style={{color: 'red'}}>{errorMessage}</div>
</React.Fragment>
<React.Fragment>를 사용해주면 개발자 창에서 봤을 때 <React.Fragment>태그가 나오지 않고 바로 <input>
태그를 보이게 할 수 있습니다.
⇒ <React.Fragment>를 <>로 생략해서 사용할 수도 있습니다.
<form onSubmit={onSubmit}>
<InputField
type="text"
value={movieTitle}
placeholder="영화제목"
onChange={e => setMovieTitle(e.target.value)}
errorMessage={titleError}
/>
<InputField
type="number"
value={movieYear}
placeholder="개봉년도"
onChange={e => setMovieYear(e.target.value)}
errorMessage={yearError}
/>
<button type="submit">영화추가</button>
</form>
InputField 컴포넌트를 사용해서 자식 컴포넌트에 데이터를 전달해줍니다.
filter() 함수는 map() 함수처럼 배열안에 있는 모든 아이템을 돌면서 filter안의 함수를 실행시켜 줍니다.
a.filter(v => {
return v.id !== 1;
})
여기서 v는 객체 자체를 뜻합니다. 위의 경우에는 객체의 id가 1이 아닌 경우에만 리턴하게 됩니다.
삭제 버튼을 클릭하면 영화가 Movie list에서 사라지도록 코드를 작성합니다.
const Movie = ({movie, removeMovie}) => {
return (
<div className="movie">
<div className="movie-title">
{movie.title}
<span className="movie-year">
({movie.year})
</span>
</div>
<div>
<button onClick={() => removeMovie(movie.id)}>
삭제
</button>
</div>
</div>
);
};
부모 컴포넌트의 removeMovie
함수를 인자로 받아옵니다. 삭제 버튼을 누르면 해당 영화의 id가 removeMovie의 인자로 들어가며 함수가 실행됩니다.
const removeMovie = (id) => {
setMovies(movies.filter(movie => {
return movie.id !== id;
}))
};
자식 컴포넌트 Movie로부터 영화 id를 전달받으면 filter() 함수로 삭제하고자 하는 영화의 id를 제외한 다른 영화들을 리턴하여 setMovies() 함수로 movies 객체 배열의 상태를 변경해줍니다. (해당 id의 영화만 삭제됩니다)