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

Time:04-10

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.

  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;

CodePudding user response:

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>

CodePudding user response:

Use Routes instead of Switch. After v5 Switch is replaced with Routes.

  • Related