채널톡 아이콘

인프런 커뮤니티 질문&답변

musful님의 프로필 이미지

작성한 질문수 3

한 번에 끝내는 자바스크립트: 바닐라 자바스크립트로 SPA 개발까지

3. 동물 앨범 만들기-2-3

TabBar.js 오류가 자꾸 발생하는데 무슨이유인지 모르겠습니다;;

해결된 질문

작성

·

64

0

전체 사진 처음 화면은 로딩 되서 잘나오는데 펭귄이나 다른탭을 클릭하면 아래와 같은 오류가 나오는데 TabBar.js onclick 함수가 아니라는데;; 왜이런건지 함수가 맞는데 아래에 제가 작성한 코드 입니다 검토 좀 부탁드립니다.^^;;

TabBar.js

export default function TabBar({ $app, initialState, onClick }) {
  // TabBar 클래스를 생성합니다. 초기값과 클릭 이벤트를 받습니다.
  this.state = initialState; // 초기값 설정
  this.onClick = onClick; // 클릭 이벤트 설정

  this.$target = document.createElement("div"); // 새로운 div 요소를 생성합니다.
  this.$target.className = "tab-bar"; // div 요소에 클래스 이름을 추가합니다.
  $app.appendChild(this.$target); // $app 요소에 div 요소를 추가합니다.

  this.template = () => {
    let temp = `<div id="all">전체</div><div id="penguin">펭귄</div>
        <div id ="koala">코알라</div><div id ="panda">판다</div>`; // 전체 탭을 추가합니다.
    return temp; // temp를 반환합니다.
  };
  this.render = () => {
    // 렌더링 함수
    this.$target.innerHTML = this.template(); // div 요소의 innerHTML을 template 함수의 반환값으로 설정합니다.
    let $currentTab = document.getElementById(this.state); // 현재 탭을 선택합니다.
    // $currentTab ? ($currentTab.className = "clicked") : ""; // 현재 탭이 존재하면 clicked 클래스를 추가합니다. 없으면 변화없음.
    $currentTab && ($currentTab.className = "clicked"); // && 연산자를 사용하여 현재 탭이 존재하면 clicked 클래스를 추가합니다.

    const $tabBar = this.$target.querySelectorAll("div"); // 모든 div 요소를 tabBar 요소에 담아온다.
    $tabBar.forEach((elm) => {
      elm.addEventListener("click", () => {
        // 각 div 요소에 클릭 이벤트 리스너를 추가합니다.
        this.onClick(elm.id); // 클릭한 div 요소의 id를 onClick 함수에 전달합니다.
      });
    });
  };

  this.setState = (newState) => {
    // state를 변경하는 함수
    this.state = newState; // state를 새로 받은 newState로 업데이트합니다.
    this.render(); // state가 변경되면 렌더링 함수를 다시 호출하여 화면을 업데이트합니다.
  };

  this.render(); // 렌더링 함수를 호출합니다.
}

App.js

import TabBar from "./components/TabBar.js"; // TabBar.js 파일을 불러옵니다.
import Content from "./components/Content.js"; // Content.js 파일을 불러옵니다.
import { request } from "./components/api.js"; // api.js 파일을 불러옵니다.

export default function App($app) {
  // App 생성자 함수를 생성합니다.
  // $app은 App 컴포넌트가 렌더링될 DOM 요소입니다.

  this.state = {
    //state 초기값 설정
    currentTab: "all", // 탭 초기값 설정 tabbar 컴포넌트에 전달할 현재 탭 데이터
    photos: [], // 사진 초기값 설정 content 컴포넌트에 전달할 사진 데이터
  };
  const tabbar = new TabBar({
    $app, // App 컴포넌트가 렌더링될 DOM 요소를 전달합니다.
    initialState: "", // 초기값 설정
    oncClick: async (name) => {
      // 클릭 이벤트 설정 변경값을 currentTab에 저장
      this.setState({
        // 클릭한 탭의 데이터를 state에 저장합니다.
        ...this.State, // 기존 state를 복사합니다. 스프레드 연산자
        currentTab: name, // 클릭한 탭의 이름을 currentTab에 저장합니다.
        photos: await request(name === "all" ? "" : name), // 클릭한 탭의 새로운 사진을 request 이름으로 함수를 불러와 저장합니다.
        // request 함수는 비동기 함수로 async await를 사용하여 데이터를 받아옵니다.
      });
    },
  });
  const content = new Content({
    $app, // App 컴포넌트가 렌더링될 DOM 요소를 전달합니다.
    initialState: [], // 초기값 설정
  });

  this.setState = (newState) => {
    // 업데이트 값을 newState로 받습니다.
    this.state = newState; // state를 새로 받은 newState로 업데이트합니다.
    tabbar.setState(this.state.currentTab); // tabbar 컴포넌트에 state를 전달합니다.
    content.setState(this.state.photos); // content 컴포넌트에 state를 전달합니다.
  };

  const init = async () => {
    //웹페이지가 로드되면 실행되는 함수
    try {
      const initialPhotos = await request(); // request 함수를 불러와 initialPhotos에 저장합니다.
      this.setState({
        // state를 initialPhotos로 업데이트합니다.
        ...this.state, // 기존 state를 복사합니다. 스프레드 연산자
        photos: initialPhotos, // initialPhotos를 photos에 저장합니다.
      });
    } catch (err) {
      console.log(err);
    }
  };
  init(); // 웹애플리케이션이 실행될때 init 함수를 실행합니다.
}

index.js

import App from "../src/App.js";
const $app = document.getElementById("app");

new App($app);

api.js

const API_URL = "https://animal-api-two.vercel.app"; // 이미지 url을 변수에 저장

// const $content = document.querySelector("div.content"); //(api 불러오는 코드만 남겨놓기 위해 삭제제)
// let template = []; // (api 불러오는 코드만 남겨놓기 위해 삭제제)

//API
export const request = async (name) => {
  const res = await fetch(name ? `${API_URL}/${name}` : API_URL); // fetch 함수를 사용하여 API_URL을 호출합니다. name이 있으면 name을 호출합니다. 없으면 API_URL을 호출합니다.
  try {
    if (res) {
      let data = await res.json();
      return data.photos;
    }
  } catch (err) {
    console.log(err);
  }
};

index.html

<!DOCTYPE html>
<head>
  <title>Animal Album</title>
  <meta charset="UTF-8" />
  <link rel="stylesheet" href="../project2/src/style.css" />
  <script type="module" src="../project2/src/index.js" defer></script>
</head>
<body>
  <div id="app">
    <!-- TAB BAR -->
    <!-- CONTENT -->
  </div>
</body>

content.js

export default function Content({ $app, initialState }) {
  this.state = initialState;
  this.$target = document.createElement("div");
  this.$target.className = "Content";
  $app.appendChild(this.$target);

  this.template = () => {
    let temp = [];
    if (this.state) {
      this.state.forEach((elm) => {
        temp += `<img src="${elm.url}"></img>`;
      });
    }
    return temp;
  };
  this.render = () => {
    this.$target.innerHTML = this.template();
  };
  this.setState = (newState) => {
    this.state = newState;
    this.render();
  };

  this.render();
}

 

콘솔 오류코드


TabBar.js:26 Uncaught TypeError: this.onClick is not a function
    at HTMLDivElement.<anonymous> (TabBar.js:26:14)

답변 2

1

musful님의 프로필 이미지
musful
질문자

오 인프런 ai 인턴이 해결해줬네요 효빈님 제 오타네요 ㅋㅋ;;

효빈 Hyobin님의 프로필 이미지
효빈 Hyobin
지식공유자

해결하셔서 다행입니다!!

1

인프런 AI 인턴님의 프로필 이미지

안녕하세요, 인프런 AI 인턴입니다. musful님, 질문 주셔서 감사합니다.

열거하신 오류는 TabBar.jsonClick 이벤트 리스너 관련 설정에서 발생하는 것으로 보입니다. TypeError: this.onClick is not a function이라는 오류는 onClick이 정의되었지만 제대로 함수로 전달되지 않았기 때문에 나타납니다.

문제 해결:

  1. 오타 확인:
    • App.js에서 TabBar 컴포넌트를 생성할 때, oncClick 대신 onClick으로 명명되었는지 확인하세요. 코드에서 오타가 있는 것 같습니다.
   const tabbar = new TabBar({
     $app,
     initialState: "",
     onClick: async (name) => { // 여기를 확인해주세요.
       this.setState({
         ...this.state,
         currentTab: name,
         photos: await request(name === "all" ? "" : name),
       });
     },
   });
  1. 함수 전달 확인:
    • onClick속성에 전달되는 것이 반드시 함수이어야 하며, 적절히 App.js 내에서 정의된 함수가 전달되었는지 확인하십시오. 이 경우 async (name) => { ... } 로 설정된 것이 맞습니다.

추가 조치:

  • 각 클래스와 함수에서 this가 기대한 대로 바인딩되고 있는지 확인하십시오. 특히 이벤트 핸들러를 화살표 함수 (name) => { ... } 형태로 사용하면 this가 사전에 정의된 컨텍스트로 바인딩됩니다.

관련 질문 URL:

이런 방식으로 코드의 각종 부분을 면밀히 검토하여 이상이 없도록 한다면 문제 해결에 도움이 될 것입니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.