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

home2 게시판 React 게시판 url 이동 detail/3 이상의 상세페이지 오류 질문입니다.

url 이동 detail/3 이상의 상세페이지 오류 질문입니다.

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

    김정환
    참가자

    Detail 컴포넌트 마운트 시 props로 신발과 신발변경 스테이트를 props로 잘 넘겨 

    useEffect로 axios로 불러온 JSON파일을 신발에 각각 spread (...)을 이용해서 잘 풀어서 다시 배열에 넣고 

    신발변경함수로 고쳐주었습니다.

     

    하지만, find 메서드를 사용한 결과에 해당 id와 params에서 받아온 id가 같은 것이 없다고 undefined가 나오면서 하단의 find 결과를 가지고 데이터를 출력하는 코드 전부 오류가 납니다.

    콘솔창에 출력결과 useEffect 내부에서 해당 props.신발을 출력하면 데이터가 추가되기 전으로 나오고 

    useEffect 밖의 스코프에서 실행하면 총 6개의 데이터가 콘솔창에 찍힙니다.

    그래서 해당 콘솔로그를 그대로 내비둔체 id를 0,1,2로 url을 입력하면

    위와 같이 처음에 렌더링 되었을때는 useEffect내 신발변경함수가 변경되지 않고 추후에 렌더링되며 적용되는 것을 볼수 있습니다.

    그래서 그런지 처음부터 url을 id가 3이상으로 입력하고 들어가면 

    위 이미지와 같이 3과 값이 같은  id 프로퍼티를 찾지못하고 있습니다.

     

    혹시나해서 동기적으로 axios를 사용하면 방지할 수 있나 생각해보고 

    useEffect(() => {
    async function fetchResult() {
    let result = await axios.get(
    "https://codingapple1.github.io/shop/data2.json"
    );
    props.shoesChange([...props.shoes, ...result.data]);
    }
    console.log(props.shoes);
    fetchResult();
    }, []);

     

    위와 같이 적용해봤지만 결과는 동일하게 나옵니다.

    useEffect 뒤에 []를 비게 두면 처음 렌더링될때 실행된다고 가르켜주셨으니까 해당 내용대로라면 

    6개의 데이터가 추가된 신발 데이터를 받아와야하는게 맞는거 같은데 혼란스럽습니다.

    질문 답변해주시면 감사하겠습니다.

    밑에는 전체 코드를 올려드리겠습니다.

    --------------------------------------------------------------------------------------------------------------------------------

    App.js

    function App() {
    let [shoes, shoesChange] = useState(shoeList);

    // function dataAdd(data) {
    // let newData = [...shoes];
    // let addData = [...newData, ...data];
    // shoesChange(addData);
    // }

    return (
    <div className="App">
    <Navbar bg="light" expand="lg">
    <Container>
    <Navbar.Brand href="#home">ShoeShop</Navbar.Brand>
    <Navbar.Toggle aria-controls="basic-navbar-nav" />
    <Navbar.Collapse id="basic-navbar-nav">
    <Nav className="me-auto">
    {/* <Nav.Link>
    <Link to="/">Home</Link>
    </Nav.Link> */}
    <Nav.Link as={Link} to="/">
    Home
    </Nav.Link>
    <Nav.Link as={Link} to="/detail">
    Detail
    </Nav.Link>
    <NavDropdown title="Dropdown" id="basic-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>
    </Navbar.Collapse>
    </Container>
    </Navbar>

    {/* Switch태그로 감싸면 중복되는 url주소 있어도 상단에 작성된 하나만 보여줌 */}
    <Switch>
    <Route exact path="/">
    <div className="background">
    <h1>20% Season off</h1>
    <p>jumbotron</p>
    <p>
    <Button variant="primary">Primary</Button>
    </p>
    </div>
    <div className="container">
    <div className="row">
    {shoes.map((item, index) => {
    return <ItemBox item={item} index={index} key={index} />;
    })}
    </div>
    <button
    className="btn btn-primary"
    onClick={() => {
    // 로딩중 UI

    // axios post 요청 = 데이터 전송
    // axios.post('서버URL', {id : "아이디", pw : 1234});

    // axios get요청
    axios
    .get("https://codingapple1.github.io/shop/data2.json")
    .then((result) => {
    // 로딩중 UI 안보이게
    //서버와 연결 성공했을때
    // result는 성공해서 받아온 모든 정보들이 담겨있는 객체
    shoesChange([...shoes, ...result.data]);
    // result.data는 결과 객체에서 데이터 프로퍼타에 해당하는 값만 가져옴
    })
    .catch(() => {
    //서버와 연결 실패했을때
    console.log("실패!");
    });
    //fetch도 위와 사용법은 거의 같음 대신 axios는 JSON파일의 "":"" 내용의 문자열에서
    // ""를 제거하여 객체 타입으로 반환해주고 fetch 함수는 JSON 그대로 반환하기때문에 객체타입으로 변환하는 작업이 필요하다.
    // fetch("https://codingapple1.github.io/shop/data2.json")
    // .then((result) => {
    // console.log(result.data);
    // })
    // .catch();
    // 위처럼 사용하면 undefined 출력됨, 객체타입으로 변환 필요
    }}
    >
    더보기
    </button>
    </div>
    </Route>
    <Route exact path="/detail/:id">
    <Detail shoes={shoes} shoesChange={shoesChange} />
    </Route>

    <Route path="/:id">
    {/* :id 는 아무문자를 의미 */}
    <div>아무거나 적었을때 이거 보여줌</div>
    </Route>
    </Switch>
    {/* <Route path="~~~" component={~~~}></Route> */}
    </div>
    );
    }

    function ItemBox(props) {
    return (
    <div
    className="col-md-4"
    onClick={() => {
    window.location.href = "/detail/" + props.item.id;
    }}
    >
    <Pointer
    src={
    "https://codingapple1.github.io/shop/shoes" +
    (props.index + 1) +
    ".jpg"
    }
    alt="상품이미지1"
    width="100%"
    />
    <h4>{props.item.title}</h4>
    <p>
    {props.item.content} & {props.item.price}
    </p>
    </div>
    );
    }

    export default App;

     

    --------------------------------------------------------------------------------------------------------------------------------

    Detail.js

    function Detail(props) {
    useEffect(() => {
    async function fetchResult() {
    let result = await axios.get(
    "https://codingapple1.github.io/shop/data2.json"
    );
    props.shoesChange([...props.shoes, ...result.data]);
    }
    console.log(props.shoes);
    fetchResult();
    }, []);

    console.log(props.shoes);

    let [alert, alertChange] = useState(true);
    let [inputData, inputDataChange] = useState("");

    // 컴포넌트가 mount 되었을때, update 될때 특정 코드 실행
    // use Effect 는 여러개 생성 가능 대신 먼저 적힌 코드부터 실행
    // use Effect 콜백함수 뒤에 인자로 []를 사용하여 안에 특정 state를 집어넣을 수 있다.
    // 즉 []는 state 조건을 뜻하고 해당 []가 비어있으면 조건에 맞는값이 없으므로
    // 해당 useEffect는 실행되지 않는다. = 해당 컴포넌트가 처음 로드될때만 실행됨
    // [] 내부에 state 여러개 추가 가능 ex) [state1, state2...]
    useEffect(() => {
    let timer = setTimeout(() => {
    alertChange(false);
    }, 2000);

    console.log("hello");

    // unmount 될때
    return () => {
    clearTimeout(timer);
    };
    // settimeout은 해당 초가 지나기 전에 사용자에 의해 페이지 변경이 일어나면
    // 버그가 발생할 수 있어 실행 후 제거해주는 것이 좋다. = clearTimeout
    }, []);

    let history = useHistory();
    let { id } = useParams();
    // useParams 반환값은 객체 그 안에 url의 모든 파라미터 담겨있음
    // 그래서 destructuring 을 사용해 변수에 담아줌

    let selectItem = props.shoes.find((i) => {
    return i.id == id;
    });
    // find 메서드는 앞에 온 배열의 하나하나를 인자로 넣어주어 뒤에 콜백함수에 적어준 조건에 맞는 첫번째 값을 반환한다.
    // 만약 조건에 맞는 모든 값들을 얻고 싶다면 filter 함수를 사용해 배열형태로 조건에 맞는 값들을 받아올 수 있다.

    console.log(selectItem);
    console.log(id);

    return (
    <div className="container">
    <Box>
    <Title className="red">Detail</Title>
    </Box>

    {inputData}
    <input
    onChange={(e) => {
    inputDataChange(e.target.value);
    }}
    />

    {
    // 보통의 UI가 이렇게 true. false를 반환하는 state를 만들고
    // 삼항조건식으로 생성 및 제거하는 방법으로 구축한다.
    alert === true ? (
    <div className="my-alert-2">
    <p>재고가 얼마 남지 않았습니다</p>
    </div>
    ) : null
    }

    <div className="row">
    <div className="col-md-6">
    <img
    src={
    "https://codingapple1.github.io/shop/shoes" +
    (selectItem.id + 1) +
    ".jpg"
    }
    width="100%"
    alt="이미지"
    />
    </div>
    <div className="col-md-6 mt-4">
    <h4 className="pt-5">{selectItem.title}</h4>
    <p>{selectItem.content}</p>
    <p>{selectItem.price}원</p>
    <button className="btn btn-danger">주문하기</button>
    &nbsp;
    <button
    className="btn btn-danger"
    onClick={() => {
    history.push("/");
    }}
    >
    뒤로가기
    </button>
    </div>
    </div>
    </div>
    );
    }

    export default Detail;

    --------------------------------------------------------------------------------------------------------------------------------

    #19432

    codingapple
    키 마스터

    Detail.js 의 useEffect 안에서 ajax 요청으로 데이터를 가져오고 있습니다 

    ajax요청 성공 후 props.shoes에 반영되기까지 오래걸리는데

    그 전에 다른 곳에서 props.shoes를 쓰고 있어서 에러가 나는 것일 뿐입니다 

     

    props.shoes를 사용하는 코드를 ajax 요청 성공 후로 옮기거나 

    props.shoes가 필요한 html들을 if문으로 "props.shoes에 원하는 항목이 있으면 이 html 보여주세요" 라고 묶어놓거나 그러면 될듯요 

     

    #19445

    김정환
    참가자

    답변 감사합니다.

    현재 다른 방법으로 해결을 했는데요.

    App.js에서 해당 아이템 목록 컴포넌트의 onclick에 useHistory라는 react-router-dom 라이브러리 기능을 사용하여 push()로 url을 이동시키니 6개 데이터가 온전히 잘 넘어갔습니다. 

     

    하지만, 이렇게되면 detail 컴포넌트에서 axios로 ajax 요청을 하지 않아야 6개의 데이터를 받아오고 요청을 하면 9개의 데이터가 되어 맨 뒤 2개의 이미지는 원래 없는 이미지로 엑박이 나타나는데 

    이 현상이 결국 useHistory를 사용하면 url을 새로고침하지 않고 push로 페이지 이동을 가능하게 해주어 

    App.js에서 ajax처리한 신발스테이트가 그래도 온전히 넘어간것으로 생각되는데 

     

    정확히 어떤원리로 넘어가는지 설명해주시면 감사하겠습니다~

    #19446

    codingapple
    키 마스터

    app.js에서 ajax요청으로 데이터를 가져오면 별문제없겠군요

    그냥 주소창에 detail/6 이렇게 입력하면 브라우저 새로고침이 되어 state도 초기화됩니다

    history.push로 이동하면 브라우저 새로고침이되지않습니다 그래서그런듯요

    #19450

    김정환
    참가자

    답변감사드립니다.

    그럼 리액트에서는 브라우저 새로고침으로 인한 state 초기화를 막기 위해 history.push를 많이 이용하는 편인가요?

    #19459

    codingapple
    키 마스터

    리액트는 html 페이지가 index.html 하나입니다

    항상그래야합니다 

     

    #19513

    김정환
    참가자

    답변 감사합니다! 도움 많이 되었습니다~

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

About

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

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

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