Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

React question(route component doesn't work in <Switch>)

I used <Switch> component, and some <Route> are working.
To use Route, I made .js file and export, import to use in App.js

<Switch>
  <Route path="/detail">
    <Detail></Detail>
  </Route>
  <Route path="/detail">
    <Detail></Detail>
  </Route>
  <Route path="/cart">
    <Cart></Cart>
  </Route>
</Switch>

However, I can see white page on http://localhost:3000/cart
Cart component doesn’t work.
When I put Cart component out of the Switch component, it works.

I did 3 steps to make and use component.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

  1. make javascript file.
import React from 'react'

function Test() {
  
  return (
    <div>
      test page
    </div>
  )
}

export default Test;
  1. import in the App.js
import Cart from './Cart.js';
  1. use component in the App.js
<Route path="/cart">
    <Test></Test>
</Route>

The main problem is that some Route component doesn’t work in Switch component.

Is there anything wrong or something to note?
Sorry for not explaining better.

==================================================================
This is the code I’m practicing right now, so it may be hard to see.
the actual codes are

App.js

import React, { useContext, useState } from 'react';
import { Navbar, Container, Nav, NavDropdown, Button } from 'react-bootstrap';

import './App.css';
import Data from './data.js';
import Detail from './Detail.js';
import axios from 'axios';
import Cart from './Cart.js';
import Test from './Test.js';

import { Link, Route, Switch } from 'react-router-dom';

// context만들기 
// 1. React.createContext(); 로 범위 생성
// 2. 같은 값을 공유할 HTML을 범위로 싸매기
// 3. useContext(범위이름)로 공유된 값 사용하기
export let stockContext = React.createContext();

function App() {

  let [shoes, setShoes] = useState(Data);
  let [stock, setStock] = useState([10, 11, 12])

  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 as={Link} to="/">Home</Nav.Link>
              <Nav.Link as={Link} to="/detail/0">Detail</Nav.Link>
              <Nav.Link as={Link} to="/cart/">Cart</Nav.Link>
              <Nav.Link as={Link} to="/test/">Test</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>
        <Route exact path="/">
          <div className="background">
            <h1>20% Season Off</h1>
            <p>
              This is a simple example.
            </p>
            <p>
              <Button variant="primary">Learn more</Button>
            </p>
          </div>

          <div className="container">

            <stockContext.Provider value={stock}>

            <div className="row">
              {
                shoes.map( (a, i)=>{
                  return <Card shoes={shoes[i]} i={i}></Card>
                })
              }
            </div>

            </stockContext.Provider>

            <button className="btn btn-primary" onClick={()=>{
              // 서버로 데이터 보내고싶을때
              // axios.post('서버url', {id : 'jino', pw : 1234})

              // 서버Url에 get요청
              axios.get('https://codingapple1.github.io/shop/data2.json')
              // 성공하면
              .then((result)=>{
                setShoes([...shoes, ...result.data]);
              })
              // 실패하면
              .catch(()=>{
                console.log('실패')
              })

            }}>더보기</button>

          </div>
        </Route>
        
        <stockContext.Provider value={stock}>
        <Route path="/detail/:id">
          <Detail shoes={shoes} stock={stock} setStock={setStock}></Detail>
        </Route>
        </stockContext.Provider>

        <Route path="/cart">
          <Cart></Cart>
        </Route>

        <Route path="/test">
          <Test></Test>
        </Route>

        
    {/* 모든 경로에서 보인다. 지금은 Switch 컴포넌트 안에있어서 Router들이 하나만보인다. */}
        {/* <Route path="/:id">
          <div>아무거나 적었을때 이것이 보인다.</div>
        </Route> */}

      </Switch>

    </div>

    
  );
}

function Card(props){
  
  return(
    <div className="col-md-4">
      <img src={"https://codingapple1.github.io/shop/shoes"+ (props.i+1) +".jpg"} width="100%" />
      <h4>{props.shoes.title}</h4>
      <p>{props.shoes.content} & {props.shoes.price}</p>
      {/* <Test></Test> */}
    </div>
    
  )
}


// function Test(){
//   let stock = useContext(stockContext);
//   return <p>재고 : {stock}</p>
// }

export default App;

Cart.js

import Recact from 'react';
import {Table} from 'react-bootstrap';


function Cart(){
  return (
    <div>
      <Table striped bordered hover>
        <thead>
          <tr>
            <th>#</th>
            <th>First Name</th>
            <th>Last Name</th>
            <th>Username</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>1</td>
            <td>Mark</td>
            <td>Otto</td>
            <td>@mdo</td>
          </tr>
          <tr>
            <td>2</td>
            <td>Jacob</td>
            <td>Thornton</td>
            <td>@fat</td>
          </tr>
          <tr>
            <td>3</td>
            <td colSpan={2}>Larry the Bird</td>
            <td>@twitter</td>
          </tr>
        </tbody>
      </Table>
    </div>
  )
}

export default Cart;
import React, { useContext, useEffect, useState } from 'react';
import { useHistory, useParams } from 'react-router-dom';
import './Detail.scss';
import {Button, Nav} from 'react-bootstrap';

import {CSSTransition} from 'react-transition-group';

import {stockContext} from './App'

function Detail(props){

  let [alert, setAlert] = useState(true);
  let stock = useContext(stockContext);
  let [tab, setTab] = useState(0);
  let [buttonSwitch, setButtonSwitch] = useState(false);
  // useEffect 훅 : 컴포넌트가 mount되었을때, update될때 특정 코드를 실행할 수 있다.
  // useEffect 여러개 사용하면 순서대로 실행
  useEffect(()=>{
  let timer = setTimeout(()=>{ setAlert(false) }, 2000);
  // 2000ms 후에 실행할 코드


    // retrun(Detail 컴포넌트가 사라질때 실행)
    // return function code(){}   
    // return ()=>(){} 와 같음  
    // 의도치 않은 error가 발생할 수 있어 clearTimeout() 함수 사용
    return ()=> {clearTimeout(timer)}

  },[alert]);
  // [] 안에 useEffect가 실행될 조건 입력 / 여기선 alert가 변경될때만 실행한다.
  //  빈[] 면 update시엔 적용하지않는다.

  

  let { id } = useParams();
  let history = useHistory();
  let product = props.shoes.find(function(shoes){
    return shoes.id == id;
  });

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

{/* alert가 true인지 ?? // ?-> 참일때 실행할 코드 / : -> 거짓일때 실행할 코드 */}
      {
        alert === true
        ? (<div className="my-alert">
            <p>재고가 얼마 남지 않았습니다.</p>
          </div>)
        : null
      }
      


          <div className="row">
            <div className="col-md-6">
              <img src={"https://codingapple1.github.io/shop/shoes"+(product.id+1)+".jpg"} width="100%" />
            </div>
            <div className="col-md-6 mt-4">
              <h4 className="pt-5">{product.title}</h4>
              <p>{product.content}</p>
              <p>{product.price}원</p>

              <Info stock={props.stock}></Info>

              <button className="btn btn-danger" onClick={()=>{
                var copyArray = [...props.stock];
                copyArray[0] -= 1;
                props.setStock(copyArray);

              }}>주문하기</button> 

              <button className="btn btn-danger" onClick={()=>{
                // history.goBack();
                history.push('/')
              }}>뒤로가기</button> 
            </div>
          </div>

          <Nav className="mt-t" variant="tabs" defaultActiveKey="link-0">
            <Nav.Item>
              <Nav.Link eventKey="link-0" onClick={()=>{
                setButtonSwitch(false);
                setTab(0);
              }}>0번째 탭</Nav.Link>
            </Nav.Item>
            <Nav.Item>
              <Nav.Link eventKey="link-1" onClick={()=>{
                setButtonSwitch(false);
                setTab(1)
              }}>1번째 탭</Nav.Link>
            </Nav.Item>  
            <Nav.Item>
              <Nav.Link eventKey="link-2" onClick={()=>{
                setButtonSwitch(false);
                setTab(2)
              }}>2번째 탭</Nav.Link>
            </Nav.Item>  
          </Nav>

              {/*   스위치(true면 동작)                동작하는 시간*/}
          <CSSTransition in={buttonSwitch} classNames="wow" timeout={500}>
            <TabContent tab={tab} setButtonSwitch={setButtonSwitch}></TabContent>
          </CSSTransition>
          



        </div>
  )
}

function Info(props){
  return (
    <p>재고: {props.stock[0]}</p>
  )
}

// 조건이 3개이상인 조건문만들기
function TabContent(props) {

  useEffect(()=>{
    props.setButtonSwitch(true);
  });

  if(props.tab===0){
    return <div>0번째 내용입니다.</div>
  } else if(props.tab===1){
    return <div>1번째 내용입니다.</div>
  } else if (props.tab===2){
    return <div>2번째 내용입니다.</div>
  }
  
}

export default Detail;

Test.js

import React from 'react'

function Test() {
  
  return (
    <div>
      test page
    </div>
  )
}

export default Test;

>Solution :

Issue

The issue is the stockContext.Provider component in the middle of the Switch component. It will always be "matched" and rendered if a route above wasn’t, and the route matching logic won’t ever reach the routes defined after it.

<Switch>
  <Route exact path="/">
    ...
  </Route>

  // Will be matched and rendered if route above not matched
  <stockContext.Provider value={stock}>
    <Route path="/detail/:id">
      <Detail shoes={shoes} stock={stock} setStock={setStock}></Detail>
    </Route>
  </stockContext.Provider>

  // Unreachable routes!!
  <Route path="/cart">
    <Cart></Cart>
  </Route>

  <Route path="/test">
    <Test></Test>
  </Route>
    
  {/* 모든 경로에서 보인다. 지금은 Switch 컴포넌트 안에있어서 Router들이 하나만보인다. */}
  {/* <Route path="/:id">
    <div>아무거나 적었을때 이것이 보인다.</div>
  </Route> */}

</Switch>

Solution

Move the stockContext.Provider out of the Switch component, or into the Route so the Route is a child of Switch component.

Example:

<stockContext.Provider value={stock}>
  <Switch>
    <Route exact path="/">
      ...
    </Route>

    <Route path="/detail/:id">
      <Detail shoes={shoes} stock={stock} setStock={setStock} />
    </Route>

    <Route path="/cart">
      <Cart />
    </Route>

    <Route path="/test">
      <Test />
    </Route>
    
    {/* 모든 경로에서 보인다. 지금은 Switch 컴포넌트 안에있어서 Router들이 하나만보인다. */}
    {/* <Route path="/:id">
      <div>아무거나 적었을때 이것이 보인다.</div>
    </Route> */}

  </Switch>
</stockContext.Provider>

or

<Switch>
  <Route exact path="/">
    ...
  </Route>

  <Route path="/detail/:id">
    <stockContext.Provider value={stock}>
      <Detail shoes={shoes} stock={stock} setStock={setStock} />
    </stockContext.Provider>
  </Route>

  <Route path="/cart">
    <Cart />
  </Route>

  <Route path="/test">
    <Test />
  </Route>
    
  {/* 모든 경로에서 보인다. 지금은 Switch 컴포넌트 안에있어서 Router들이 하나만보인다. */}
  {/* <Route path="/:id">
    <div>아무거나 적었을때 이것이 보인다.</div>
  </Route> */}

</Switch>
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading