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.
- make javascript file.
import React from 'react'
function Test() {
return (
<div>
test page
</div>
)
}
export default Test;
- import in the App.js
import Cart from './Cart.js';
- 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.