본문 바로가기

TIL

[TIL-133~134] 위코드 34~5일차: React JS - Monster

React JS

 

Monster 과제

useEffect() 훅을 사용하여 데이터 로드(fetch)하기

useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/users", { method: "GET" })
      .then((res) => res.json())
      .then((result) => setMonsters(result));
}, []);
  • useEffect( () => { 실행할 코드 }, [ 이 안의 state 중 뭔가가 변경되면 useEffect()가 실행됨 ] ) : 콜백함수와 state들을 요소로 갖는 배열을 인자로 갖는다. 두번째 인자인 배열을 비우면 페이지가 로드될 때 최초 1회만 실행된다.
  • fetch( "API 주소", { method: "", 등 서버로 보낼 정보 } )
  • .then( (앞에서 코드가 실행한 뒤 리턴한 값) => { 실행할 코드 & return } )
  • json() : json 형식으로 바꿔주는 메서드.
  • [주의사항] useEffect() 안에 fetch()도 쓰다보니 (), {} 등을 여닫는 게 헷갈린다. 어디서부터 어디까지가 인자고, 함수인지 잘 확인하기.
  • [의문] console.log(monsters)를 useEffect 안 콜백함수에서 찍었더니 안나오고 useEffect 밖에서 찍으니까 안 나옴.

 

map() 메서드와 props를 사용하여 컴포넌트 재사용하기

return (
    <div className="monsters">
      <h1>컴포넌트 재사용 연습!</h1>
      // CardList 컴포넌트로 monsters 스테이트 값을 props로 전달
      <CardList monsters={monsters} />
    </div>
);

---

function CardList({ monsters }) {
  return (
    <div className="cardList">
      // 전달받은 monsters 배열에 map() 사용하여
      // 각각의 monster 정보를 담은 객체를 props로 갖는 Card 컴포넌트 리스트 생성
      {monsters.map((monster) => (
        <Card key={monster.id} monster={monster} />
      ))}
    </div>
  );
}

---

function Card({ monster }) {
  return (
    <div className="cardContainer">
      <img
        alt="monster"
        src={`https://robohash.org/${monster.id}?set=set2&size=180x180`}
      />
      <h2>{monster.name}</h2>
      <p>{monster.email}</p>
    </div>
  );
}

 

 

filter() 메서드를 사용하여 검색 기능 추가하기

function handleChange(event) {
    setUserInput(event.target.value);
}

const filteredMonsters = monsters.filter((monster) => monster.name.toLowerCase().includes(userInput.toLowerCase()))

return (
    <div className="monsters">
      <h1>컴포넌트 재사용 연습!</h1>
      <SearchBox handleChange={handleChange} />
      <CardList monsters={userInput ? filteredMonsters : monsters} />
    </div>
);
  • filter() 메서드 : 콜백함수를 인자로 받음. 콜백함수는 filter 메서드가 실행된 배열의 각 요소를 인자로 받아 주어진 조건을 검사함. filter()는 배열의 각 요소에 대해 콜백함수를 실행하여 true를 반환하는 요소만 새로운 배열에 담아 반환함.

성공적으로 검색된 화면

  • 시행착오
    • 문제 : 처음에 "monsters" state 값에서 filter를 써서 필터된 배열로 setMonsters 해주었는데, 그럼 "monsters" 배열의 요소(객체)가 사라져서 검색창의 input value를 지워도 원래의 "monsters" 정보를 되돌릴 수 없음. 심지어 왠지 모르게 input 값을 받는 게 한 박자 느려서 글자를 2자 이상 써야 작동하고, 한 글자 이전까지 받아들임.
    • 그래서 fetch 후 "monsters" 값을 받을 때, 최초의 "monsters" 값을 "initMonsters"라는 변수로 저장해두고 사용하고 싶었음.
      • 하지만 useEffect()나 fetch() 후 then() 안에서는 변수를 선언해도 밖에서 쓸 수 없음. 함수 스코프를 갖는 let/const 변수가 해당 함수 안에서 갇히기 때문인 것 같음.
      • let initMonsters = [];
        
        useEffect(() => {
            fetch("https://jsonplaceholder.typicode.com/users", { method: "GET" })
              .then((res) => res.json())
              .then((result) => {
                initMonsters = result;      // 여기서 혹은
                setMonsters(result);
              });
            initMonsters = monsters;        // 여기서 할당
        }, []);
        
        function handleChange(event) {
            setUserInput(event.target.value);
            setMonsters(
              initMonsters.filter((monster) =>
                monster.name.toLowerCase().includes(userInput.toLowerCase())
              )
            );
        }
        
        return (
            <div className="monsters">
              <h1>컴포넌트 재사용 연습!</h1>
              <SearchBox handleChange={handleChange} />
              <CardList monsters={monsters} />
            </div>
        );
      • 그래서 Monster 컴포넌트 내 최상위 위치에서 "initMonsters" 변수를 let 키워드로 선언하고, useEffect() 안에서 데이터를 로드한 뒤 받아온 monsters 배열을 값으로 재할당해보았음. 하지만 다시 렌더링될 때 useEffect()를 거치지 않으면서 "initMonsters" 변수의 값이 다시 비게 됨.
        • Assignments to the 'initMonsters' variable from inside React Hook useEffect will be lost after each render. To preserve the value over time, store it in a useRef Hook and keep the mutable value in the '.current' property. Otherwise, you can move this variable directly inside useEffect.
    • 해결
      • 가이드대로 "monsters" state는 그냥 두고, 새로운 변수 "filteredMonsters"에 filter되어 반환되는 값을 저장함.
      • handleChange 함수에서는 input 값만 "userInput" state에 저장함.
      • "userInput" 값이 있을 때(검색창에 뭔가 입력했을 때) "filteredMonsters"를, 그렇지 않을 때(검색하고 있지 않을 때) "monsters" state를 CardList 컴포넌트에 props로 넘겨줌.

 

 

참고) Javascript - Array filter 사용법 https://7942yongdae.tistory.com/49

 

 


공부할 것

React Hooks 이해하기 (1) https://velog.io/@gwak2837/React-Hooks%EC%9D%98-%EC%9D%B4%ED%95%B4