[Mini Project] Monster - Class

김찬영·2020년 12월 19일
0

Mini Project

목록 보기
1/4
post-thumbnail

구현하는 기능과 배우는 컨셉

동적 라우팅 사용하기
카드를 클릭했을때 해당카드의 디테일 페이지로 이동
이동하고나서, 바로전 페이지로 이동할 수있는 BackBtn
PrevPros

쉽게 생각해보자!!

1. 카드를 클릭했을때 해당카드의 디테일 페이지로 이동

2. 이동하고나서, 바로전 페이지로 이동할 수있는 BackBtn

3. Card Prev Btn , Next Btn !!

동적 라우팅 사용하기 (이동해야하니깐!!)


---Route.js-----
  <Router>
        <Switch>
          <Route exact path="/monsters" component={UrlParameters} />
          <Route exact path="/monsters/detail/" component={MonsterDetail} />
          <Route exact path="/monsters/detail/:id" component={MonsterDetail} />
        </Switch>
      </Router>

라우팅을 사용하려면 기본적으로 해당 경로를 설정해야한다.

  • <Route <Route exact path="/monsters" component={UrlParameters} />에서 exact path는 정확한 경로를 나타내고, component는 이동시킬 컴포넌트르 작성하면된다.

  • 쉽게말해서 해당 경로로가면 해당컴포넌트로 이동하는것!!

Card에는 라우터를 따로 설정하지않았음!

  render() {
    const {id, name, email} = this.props;
    console.log(this.props.id);
    return (
      <div className="card-container" onClick={this.goToDetail}>
        <img src={`https://robohash.org/${id}?set=set2&size=180x180`}></img>
        <h2> {name}</h2>
        <p> {email}</p>
      </div>
    );
  }
  export default withRouter(Card);

카드리스트안에서 보여주는 카드의 컴포넌트는 따로 라우트를 설정하지않았기 때문에 대신 withRouter르 설정해주었다. withRouter를 설정하면 이따 배울 histroy,match,location을 사용할 수있음!!

Card를 누르면 이동설정하기!

  • 위의코드를 보면 card-container를 눌렀을때 histroy를 이용해서 이동하는 것을 볼수있다.
  • 여기서 ("/monsters/detail/:1") 이런식으로 데이터들의 id값을 이용해서 경로를 설정해줄 수 있다.
  • 하지만 유동적으려 변하기위해선 백틱을 이용해서 {this.props.id}를 사용하자!
  • this.props.id는 monster.js에서 필터링된 데이터가 들어있는 id이다. 즉, search에서 입력한 데이터들의 카드만 UI에 표시하고 ID를 맵핑이 돌때마다 할당이되는것이다
  • ex) l를 입력했을때 카드가 3개가 뜬다면, 3개의 각각 ID를 전달받고 카드리스트에서 맵핑한 id가 하나씩 Card컴포넌트에 들어가서 history에 this.props.id에 할당되는것이다 (총 3번)

여기서 잠깐~!!!! 그럼왜 첫 랜더링화면은 모든 카드데이터가 보여지는것일까??

----Monster.js------
 const {userInput, monsters} = this.state;
    const filteredData = monsters.filter((data) => {
      return (
        data.name.toLocaleLowerCase().includes(userInput.toLocaleLowerCase()) ||
        data.email.toLocaleLowerCase().includes(userInput.toLocaleLowerCase())
      );
    });
    return (
      <div className="Monsters">
        <h1>컴포넌트 재사용 연습!</h1>
        <SearchBox handleChange={this.handleChange} />
        <CardList monsters={filteredData} />
        {/* <SearchBox handleChange=정의한메소드 /> */}
        {/* <CardList monsters=몬스터리스트 /> */}
      </div>
    );

여기서 조건문을보자!! 처음 랜더링할때는 입력하지않았으니 userInput은 빈 문자열인 ''가 들어있다. includes 함수는 빈 문자열도 true로 반환하기떄문에!! 실행이되는 것이고, 입력하지않았으니 data.name 전체가 filteringData에 할당되는것이다!

CardDetail의 UI 나타내자!

Card를 누르면 이동은하지만 CardDetail은 해당데이터를 UI에 표시하지못한다. 그이유는 CardDetail에 어떤 API Data값도 받지 못했기때문이다. 그러므로 fetch로 data를 할당해주자!

state = {
    data: {},
  };
  getData = () => {
    fetch(
      `https://-/users/${this.props.match.params.id}`
    )
      .then((res) => res.json())
      .then((res) =>
        this.setState({
          data: res,
        })
      );
  };
  componentDidMount() {
    this.getData();
  }

API에서 불러온 데이터에 아까 경로에서 설정한 monsters/detail를 쉽게 전달할 수 있는 방법이 바로 this.props.match.params이다!! 이것을 통해서 monsters/detail이 들어오고

http~~/users/monsters/detail.id 이렇게 들어올 수 있는것이다.

위에서 확실하게 볼수있다. Card에서 push(/monsters/detail/${this.props.id});를 하게되면 id값이 주소 경로에 뜨고 그경로를 props.match를 통해 id값을 읽어서 id값에 해당되는 data들을 불러올 수있는것이다.

이제 불러온 data를 state에 보관하고 UI에 보여주기만 하면 된다!!


---CardDetail-----
goToDetail = () => {
    this.props.history.push(`/monsters/detail/${this.props.id}`);
  };

render() {
    const {id, name, email} = this.state.data;

    return (
      <div className="url-parameters">
        <div className="btn-wrapper">
          <button onClick={this.goToMonstersList}>Back to Monsters List</button>
        </div>
        <Card id={id} name={name} email={email} />
        <div className="btn-wrapper">
          <button onClick={this.goToPrevious}>Previous</button>
          <button onClick={this.goToNext}>Next</button>
        </div>
      </div>
    );
  }
}

위의 코드를 보면 data를 활용해서 각각 Card에 알맞게 id, name, email를 할당한다. 참고로여기서 id를 주는건 CardDetail에 나타나는 Card에 주는것이다. 아까 CardList에서 나타내는 Card 컴포넌트랑 다름을 구분하자.

CardDetail의 BackBtn 나타내자!

goToMonstersList = () => {
    this.props.history.push('/');
  };

너무너무 간단하다!! 버튼을 눌렀을때 다시 초기 화면의 경로를 설정해주면끝!!

CardDetail의 preBtn 나타내자!

  goToPrevious = () => {
    this.props.history.push(
      `/monsters/detail/${this.props.match.params.id - 1}`
    );
  };

id의 -1를 해주면 바로뒤에 있는 카드경로로 이동할 수있다.

CardDetail의 NextBtn 나타내자!


goToNext = () => {
    this.props.history.push(
      `/monsters/detail/${+this.props.match.params.id + 1}`
    );
  };

id의 +1를 해주면 바로 앞에있는 카드경로로 이동할수있다. 여기서 주의할점은 string이기 때문에 앞에 +로 Number로 변경후 더해주어야한다.

이대로 끝?? 문제가 있다!

next, prev 버튼을 누를때마다 id값이 변하지만 데이터는 변하지않는다. 데이터가 바뀔때 데이터를 다시 받아와야한다. 컴포넌트디드마운트는 한번 받고, 그다음부터 데이터를 바꿔주려주면 컴포넌트디드업데이트를 사용한다. URL은 바뀌지만 데이터가 안바뀌는것은 데이터를 패치하지 않아서 그렇다. 디드업데이트는 랜더가 되고 state가 바뀔때마다 업데이트가 실행된다. 그러므로 아무런조건없이 setState를 작성하면 계속 컴포넌트업데이트가 실행되어 패치를 계속 받아오기때문에 무한로프가 실행된다. 그래서 조건을 설정해주자!

 componentDidUpdate(prevProps, _) {
    if (prevProps.match.params.id !== this.props.match.params.id) {
      this.getData();
    }
  }

컴포넌트디드업데이는 prevProps, prevState의 인자를 받아온다. 이전프롭스와 이전 스테이트를 받아온다. (참고로 자리를 비울땐 _ 로 표시할 수있다).

이전의 props의 match.params.id와 컴포넌트디드마운트에서 패치로 받아온 props.match.params.id가 다르면 패치를 받아오면된다!

prev, next를 누르면 params.id가 변경되기 때문에 이전이랑 다를 수 밖에없다.!!

profile
Front-end Developer

0개의 댓글

관련 채용 정보