[React] 자식 컴포넌트에서 부모 컴포넌트 State 변경

몽이·2021년 11월 8일
0

React

목록 보기
10/17

자식 컴포넌트에서 부모 컴포넌트 State 변경

자식 컴포넌트에서 변경된 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>
);
  • 자식 컴포넌트의 addMovie() 함수에서 변경된 state 데이터를 받아오기 위해 props형식으로 부모컴포넌트에서 자식 컴포넌트로 addMovie() 함수 자체를 전달해줍니다. ⇒ <MovieForm addMovie={addMovie}/>
  • 자식 컴포넌트에서는 받아온 addMovie() 함수로 state를 변경하고, 부모 컴포넌트에서는 변경된 내용을 props로 받아와 addMovie() 함수의 인자로 변경된 내용인 movie를 받아와 영화 리스트에 추가해줍니다.

Form validation(유효한 영화 추가)

영화를 추가할 때 아무 정보도 입력하지 않으면 영화 제목과 개봉년도가 나오지 않고 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>
);

입력창 아래에 각각의 에러문구가 뜨게 됩니다. 유효한 경우에는 에러 문구가 빈 문자열이기 때문에 화면상에 아무런 에러문구가 뜨지 않습니다. 하지만, 유효하지 않은 경우에는 각각 titleErroryearError가 화면상에 나타납니다.

style을 사용해 에러 문구 색상을 빨간색으로 변경해줍니다.

const onSubmit = (event) => {
  // refresh 막기 위해 사용
  event.preventDefault();
  if (validateForm()) {
      addMovie({
          // 식별 가능한 id 추가
          id: Date.now(),
          title: movieTitle,
          year: movieYear,
      });
      resetErrors();
      resetForm();
  }
};

유효한 영화 정보를 입력하고 정보를 추가했으면, resetErrors() 함수로 에러문구를 빈 문자열로 변경해주고, resetForm() 함수로 입력창을 비워줍니다.

InputField 컴포넌트로 빼내기

<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가 아니어도 됩니다.)

  • root element

컴포넌트를 사용할 때 루트 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()

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의 영화만 삭제됩니다)

profile
풀스택 개발자가 되는 날까지 달리자!

0개의 댓글