[REACT] 10_상세페이지 작성 1

2021. 12. 27. 13:10React/React 쇼핑 앱

반응형

상품 상세페이지(Detail.js)에 부트스트랩 TABS 컴포넌트 삽입

https://react-bootstrap.netlify.app/components/navs/#tabs

 

React-Bootstrap

The most popular front-end framework, rebuilt for React.

react-bootstrap.github.io

<Nav variant="tabs" defaultActiveKey="/home">
  <Nav.Item>
    <Nav.Link href="/home">Active</Nav.Link>
  </Nav.Item>
  <Nav.Item>
    <Nav.Link eventKey="link-1">Option 2</Nav.Link>
  </Nav.Item>
  <Nav.Item>
    <Nav.Link eventKey="disabled" disabled>
      Disabled
    </Nav.Link>
  </Nav.Item>
</Nav>

 

CSSTranstition 라이브러리 추가

yarn add react-transition-group

http://reactcommunity.org/react-transition-group/css-transition

 

React Transition Group

The animation classNames applied to the component as it appears, enters, exits or has finished the transition. A single name can be provided, which will be suffixed for each stage, e.g. classNames="fade" applies: fade-appear, fade-appear-active, fade-appea

reactcommunity.org

 

Detail.js

더보기
import { useParams, useHistory } from "react-router-dom";
import { Nav } from 'react-bootstrap';
import { useEffect, useState } from "react";
import { CSSTransition } from "react-transition-group";
import './Detail.css';
function Detail(props) {
  let { id } = useParams();
  let foundObj = props.shoes.find((obj) => {
    return obj.id == id;
  });
  console.log(foundObj);
  let history = useHistory();
  let [tabIdx, setTabIdx] = useState(0);
  let [transSw, setTransSw] = useState(false);
  return (
    <div className="container">
      <div className="row">
        <div className="col-md-6">
          <img src={foundObj.image} alt="이미지" width="100%" />
        </div>
        <div className="col-md-6 mt-4">
          <h4 className="pt-5">{foundObj.title}</h4>
          <p>{foundObj.name}</p>
          <p>{foundObj.content}</p>
          <p>{foundObj.price}</p>
          <a class="btn btn-primary" role="button">장바구니</a>
          &nbsp;
          <a class="btn btn-secondary" role="button" onClick={() => {
            history.goBack();
          }}>뒤로가기</a>
        </div>
        {/* tab menu */}
        <Nav variant="tabs" defaultActiveKey="link-0">
          <Nav.Item>
            <Nav.Link eventKey="link-0" onClick={() => {
              setTransSw(false);
              setTabIdx(0);
            }}>TAB 1</Nav.Link>
          </Nav.Item>
          <Nav.Item>
            <Nav.Link eventKey="link-1" onClick={() => {
              setTransSw(false);
              setTabIdx(1);
            }}>TAB 2</Nav.Link>
          </Nav.Item>
        </Nav>
        {/* tab content */}
        <CSSTransition in={transSw} classNames="trans" timeout={200}>
          <TabContent tabIdx={tabIdx} setTransSw={setTransSw} />
        </CSSTransition>
      </div>
    </div>
  )
}

function TabContent(props) {
  useEffect(() => {
    props.setTransSw(true);
  });
  return (
    <>
      <div>{props.tabIdx} 번째 탭 선택됨</div>
    </>
  )
}

export default Detail;

 

Detail.css

더보기
.trans-enter {
  opacity: 0;
}
.trans-enter-active {
  opacity: 1;
  transition: opacity 200ms;
}

 

App.js

Detail 태그에 shoes 라는 속성값도 전달한다.

더보기
import React from 'react';
import { Container, Navbar, Nav, NavDropdown } from 'react-bootstrap';
import './App.css';
import Shoes1 from './api/shoes1.json';	
import { useState } from 'react';
import axios from 'axios';
import { Route, Switch } from 'react-router-dom';
import Detail from './Detail';
import { useHistory } from 'react-router-dom/cjs/react-router-dom.min';

function App() {
  let [shoes, setShoes] = useState(Shoes1);
  let [pageIdx, setPageIdx] = useState(2);
  
  return (
    <div className="App">
      {/* navbar */}
      <Navbar collapseOnSelect expand="lg" bg="dark" variant="dark">
        <Container>
        <Navbar.Brand href="#home">React-Bootstrap</Navbar.Brand>
        <Navbar.Toggle aria-controls="responsive-navbar-nav" />
        <Navbar.Collapse id="responsive-navbar-nav">
          <Nav className="me-auto">
            <Nav.Link href="/">Home</Nav.Link>
            <Nav.Link href="#pricing">Pricing</Nav.Link>
            <NavDropdown title="Dropdown" id="collasible-nav-dropdown">
              <NavDropdown.Item href="#action/3.1">Action</NavDropdown.Item>
              <NavDropdown.Item href="#action/3.2">Another action</NavDropdown.Item>
              <NavDropdown.Item href="#action/3.3">Something</NavDropdown.Item>
              <NavDropdown.Divider />
              <NavDropdown.Item href="#action/3.4">Separated link</NavDropdown.Item>
            </NavDropdown>
          </Nav>
          <Nav>
            <Nav.Link href="#login">로그인</Nav.Link>
            <Nav.Link eventKey={2} href="#join">
              회원가입
            </Nav.Link>
          </Nav>
        </Navbar.Collapse>
        </Container>
      </Navbar>
      {/* jumbotron */}
      <Switch>
        <Route exact path='/'>
          <div class="bg-light p-5 rounded-lg m-3">	
            <h1 class="display-4">Hello, world!</h1>	
            <p class="lead">This is a simple hero unit, a simple jumbotron-style component for calling extra attention to featured content or information.</p>	
            <hr class="my-4" />	
            <p>It uses utility classes for typography and spacing to space content out within the larger container.</p>	
            <a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a>	
          </div>	
          {/* card components */}
          <div className='container'>
            <div className='row'>
              {
                shoes.map((obj, i) => {
                  return <Card key={i} i={i} shoes={shoes} />
                })
              }
            </div>
          </div>
          {/* 더보기 */}
          <a class="btn btn-secondary btn" role="button" onClick={() => {
            axios.get('https://ksky216.github.io/json/data' + pageIdx + '.json').then((resp) => {
              setShoes([
                ...shoes,
                ...resp.data
              ]);
              setPageIdx(pageIdx + 1);
            }).catch((e) => {
              console.log(e);
            });
          }}>더보기</a>
        </Route>
        <Route exact path="/detail/:id">
          <Detail shoes={shoes} />
        </Route>
      </Switch>
    </div>
  );
}

function Card(props) {
  let history = useHistory();
  return (	
    <>	
      <div className='col-md-4' onClick={() => {
        history.push("/detail/" + props.shoes[props.i].id);
      }}>
        <img src={ props.shoes[props.i].image } alt="신발" width="100%" />	
        <h3>{ props.shoes[props.i].title }</h3>	
        <p>{ props.shoes[props.i].content } & { props.shoes[props.i].price }</p>	
      </div>	
    </>	
  )	
}	

export default App;

반응형