• 로그인
  • 장바구니에 상품이 없습니다.

home2 게시판 React 게시판 localStorage 를 활용한 최근 본 상품

localStorage 를 활용한 최근 본 상품

5 글 보임 - 1 에서 5 까지 (총 5 중에서)
  • 글쓴이
  • #13835

    김경원
    참가자

    안녕하십니까 강사님. 몇시간째 해매고 있어서 궁금한 점이 있어서 여쭙니다. 

    저는 최근 본 페이지를 <Show> 컴포넌트로 구현하기 위해서 props로 shoes state와 localStorage의 데이터를 arr이름으로 전송하였습니다. 

    궁금한 점

    TypeError: Cannot read property 'map' of null  -> 다음과 같은 에러가 계속 뜹니다. JSX 내에서 JS파일을 이용하여 반복문을 돌리기 위해서는 { } 중괄호 내부에 map 함수를 써서 반복문을 돌리라고 배웠는데 계속 다음과 같은 에러가 나서 답답합니다.
    처음에는 최근 본 상품을 띄우기 위해서 Show 컴포넌트 내부의 map 반복문에서 <p> { props.shoes[value].title  }</p> 과 같이 작성하였는데 title을 읽을수 없다는 둥... 계속해서 에러가 납니다. 혹시라도 map함수의 콜백함수 내부 인자인 value가 자료형이 String( Storage는 문자형이기 때매 ) 인가 싶어서 let Number = Number(value) 로 해결해보려했지만 이상한 에러만 계속 추가 되어서 여쭙습니다. 어떤게 잘못된 것인지 모르겠습니다.....  

     

    [제가 만든 Show 컴포넌트]

     

    function Show(props){
      let history = useHistory(); 
      console.log(props.shoes); 
      return (
        <div className='show'>
          <h3>최근 본 상품</h3> 
          {
            props.arr.map((value)=>{ 
              return (
                <div className = 'show-product'>
                  <p onClick={
                    ()=>{
                      history.push('/detail/${value}');
                    }
                  }>{value} 번째 상품</p>
                </div>
              )
            })
          }
        </div>
      )
    }

     

    [Detail.js 에서 show 컴포넌트 부르기]

    <Show shoes={props.shoes} arr={JSON.parse(localStorage.getItem('show'))}></Show>

     

     

    [Detail.js 전체]

    import React, { useContext, useEffect, useState } from 'react'; 
    import { useHistory, useParams } from 'react-router-dom'; 
    import styled from 'styled-components'; 
    import './Detail.scss';
    import { 재고Context } from './App.js'; 
    import { Nav } from 'react-bootstrap'; 
    import { CSSTransition } from 'react-transition-group'; 
    import { connect } from 'react-redux';

    // 스타일 컴포넌트
    let 박스 = styled.div'
      width : 100%;

    ';
    let 제목 = styled.h4'

      padding-top : 8px; 
      font-size : 30px;
      color : ${ props => props.색상 }
    ';

    // props : shoes, 재고, 재고변경()
    function Detail(props){

      // 재고 Context 범위 내에 있기 때문에 useContext로 받아와서 사용
      let 재고 = useContext(재고Context);
      
      let [알림창, 알림창변경] = useState(true); 
      // 해당 Detail 컴포넌트가 렌더링 될때 실행되는 코드

      let [input, input변경] = useState();
      let [누른탭, 누른탭변경] = useState(0); 

      let [스위치, 스위치변경] = useState(false); 

        let { id } = useParams(); 
        // params를 통해서 사용자가 detail/2라고 적었다면 2를 id 값으로 받아와서 데이터 바인딩 할수 있게끔 사용해주는 변수
        // detail/2 접속시 => props.shies[id].price 
        // 하드코딩을 통한 데이터 바인딩이 아니라 유동적으로 user가 입력한 값을 받아와서 사용이 가능하다. 
        // {id, id2} 를 통해서 사용자가 입력한 값을 가져 올 수 있다. 

        let history = useHistory(); 
        // 방문 기록을 저장해 놓는 Object, goBack()을 통해서 뒤로가기 버튼 개발

        // 가격 정렬 이후에 영구적인 id와 user가 URL창에 입력한 id와의 비교를 통해 같은 페이지를 detail 페이지에 전송
        let 찾은상품 = props.shoes.find((상품)=>{
          return 상품.id == id;
        });

        var arr; 

        useEffect(()=>{
          let 타이머 = setTimeout(()=>{
            //2초 후에 alert-box 사라지게
            알림창변경(false); 
          },1000);

          arr = localStorage.getItem('show');  
          if (arr===null){
            arr = []; 
          }else {
            arr = JSON.parse(arr);
          }
          arr.push(id);
          arr = new Set(arr); // 중복 제거 
          arr = [...arr];  
          console.log(arr); 
          localStorage.setItem('show', JSON.stringify(arr));
        },[]);
      
        // function 장바구니추가(추가상품){
        //    props.dispatch({type : '장바구니추가', payload : {추가상품 : '${추가상품}'}});
        // }

        return (

        <div className="container">
          <박스>
            <제목 className='my-detail'>Detail Page</제목>
          </박스>

          {
            알림창 === true
            ?<div className = 'my-alert'>
            상품 재고 {props.재고[찾은상품.id]}개 남았습니다!
            </div> 
            : null
          }
          
          <div className="row">
            <div className="col-md-6">
              <img src={'../이미지파일/shoes'+ (찾은상품.id+1) +'.jpg'} alt='dsa' width="100%" />
            </div>
            <div className="col-md-6 mt-4">
              <h4 className="pt-5">{찾은상품.title}</h4>
              <p>{찾은상품.content}</p>
              <p>{찾은상품.price}</p>
              <Info 재고={props.재고} 찾은상품={찾은상품}></Info>
              <p>여기는 useContext를 사용한 재고 {재고[찾은상품.id]}</p> 
              <button className="btn btn-danger btn-layout" onClick = {()=>{
                history.goBack(); 
              }}>뒤로가기</button> 
              <button className="btn btn-danger btn-layout" onClick={()=>{
                let tempArray = [...props.재고];
                tempArray[찾은상품.id] = props.재고[찾은상품.id] - 1;
                props.재고변경(tempArray); 
                props.dispatch({ type : '장바구니추가', payload : { id : '${props.state.length}', name : '${찾은상품.title}', quan : 1}})
                history.push('/cart');
              }}>주문하기</button> 
            </div>
          </div>

          <Show shoes={props.shoes} arr={JSON.parse(localStorage.getItem('show'))}></Show>

          {/* Tab 기능 개발하기, 1번 누르면 1번에 대한 내용이 뜨게 개발한다.  */}
          <Nav className='mt-5' variant="tabs" defaultActiveKey="link-0">
            <Nav.Item>
              <Nav.Link eventKey="link-0" onClick = {()=>{ 스위치변경(false); 누른탭변경(0)}}>Option 0</Nav.Link>
            </Nav.Item>
            <Nav.Item>
              <Nav.Link eventKey="link-1" onClick = {()=>{ 스위치변경(false); 누른탭변경(1)}}>Option 1</Nav.Link>
            </Nav.Item>
            <Nav.Item>
              <Nav.Link eventKey="link-2" onClick = {()=>{ 스위치변경(false); 누른탭변경(2)}}>Option 2</Nav.Link>
            </Nav.Item>
          </Nav>

          <CSSTransition in={스위치} classNames='wow' timeout={2000}>
            <Tab 누른탭={누른탭} 스위치변경={스위치변경}></Tab>
          </CSSTransition>
        
        </div> 
        );
    }

    function Show(props){
      let history = useHistory(); 
      console.log(props.shoes); 
      return (
        <div className='show'>
          <h3>최근 본 상품</h3> 
          {
            props.arr.map((value)=>{ 
              return (
                <div className = 'show-product'>
                  <p onClick={
                    ()=>{
                      history.push('/detail/${value}');
                    }
                  }>{value} 번째 상품</p>
                </div>
              )
            })
          }
        </div>
      )
    }

    function Tab(props){

      // 컴포넌트가 로드가 될때
      useEffect(()=>{
        props.스위치변경(true); 
      })

      if (props.누른탭 === 0){
        return (
          <div>0번째 탭 내용임 ㅅㄱ</div>
        );
      }else if(props.누른탭 === 1){
        return (
          <div>1번째 탭 내용임 ㅅㄱ</div> 
        );
      }else if(props.누른탭 === 2){
        return (
          <div>2번째 탭 내용임 ㅅㄱ</div>
        );
      }
    }

    function Info(props){
      return (
        <p>여기는 3중 props를 사용한 재고 : { props.재고[props.찾은상품.id] } </p>
      );
    }

    // store 에 저장되어 있는 state를 props화 시키는 작업
    function state를props화(state){
      return{
        state : state.reducer, 
        alert초기값 : state.reducer2
      }
    }

    export default connect(state를props화)(Detail); 

     

    #13842

    codingapple
    키 마스터

    Cannot read property 'map' of null 이건 map 함수 왼쪽에 있는 자료가 아무것도 없을 때 일어나는 에러입니다 

    arr 이걸 props로 잘 전송했는지 확인해야하고

    로컬스토리지에 show라는 이름의 항목이 잘 있는지도 확인해야합니다 없으면 null이 남을걸요

    #15344

    김남용
    참가자

         <Show shoes={props.shoes}
                arr={JSON.parse(localStorage.getItem('watched'))}>
              </Show>

     

    이렇게 하니까 되는데요???

    #15345

    김남용
    참가자

        <Show shoes={props.shoes}
                arr={JSON.parse(localStorage.getItem('watched'))==null?[]:JSON.parse(localStorage.getItem('watched')) }>
              </Show>

     

    이렇게 수정해보세요..

    #15421

    김경원
    참가자

    오...... 너무 감사드립니다 ㅠㅜㅠㅠ

5 글 보임 - 1 에서 5 까지 (총 5 중에서)
  • 답변은 로그인 후 가능합니다.

About

현재 월 700명 신규수강중입니다.

  (09:00~20:00) 빠른 상담은 카톡 플러스친구 코딩애플 (링크)
  admin@codingapple.com
  이용약관, 개인정보처리방침
ⓒ Codingapple, 강의 예제, 영상 복제 금지
top

© Codingapple, All rights reserved. 슈퍼로켓 에듀케이션 / 서울특별시 강동구 고덕로 19길 30 / 사업자등록번호 : 212-26-14752 온라인 교육학원업 / 통신판매업신고번호 : 제 2017-서울강동-0002 호 / 개인정보관리자 : 박종흠