React Router V4 isnt working properly - javascript

I am making a web application in which iI want to display the details of the user when clicked on them in the same page using routers.
here is my index.js page
window.React = React;
render(<div>
<Menu/><MainMenu/><App/><Footer/>
</div>, document.getElementById('react-container'))
This is my App.js Page
class App extends Component {
render () {
return (
<div>
<BrowserRouter>
<Side>
<Route path="/" component={Side}>
<Route exact path="/" component={Home}/>
<Route path="/user-lists" component={Table}>
</Route>
</Route>
</Side>
</BrowserRouter>
</div>
)
}
}
export default App
this is my users page
export default class Table extends React.Component {
constructor(props) {
super(props);
this.columns = [
{
name: "ID",
key: "id"
}, {
name: "Name",
key: "name"
}, {
name: "Username",
key: "username"
}, {
name: "Email",
key: "email"
}, {
name: "Website",
key: "website"
}
];
this.maxItems = 5;
};
state = {
pgNo: 0,
table: [],
isFetching: true,
url:"https://jsonplaceholder.typicode.com/users/"
};
componentDidMount() {
fetch("https://jsonplaceholder.typicode.com/users")
.then(response => response.json())
.then(res => {
this.setState({table: res, isFetching: false});
});
}
render() {
return this.state.isFetching
? (
<div
className="loader"
style={{
marginLeft: "50%"
}}>
<img src="/assets/index.svg"/>
</div>
)
: (
<MyTable pgNo ={this.state.pgNo}
maxItems = {this.maxItems}
columns={this.columns}
data={this.state.table}
url={this.state.url}/>
)
}
}
Here is my Sidebar.js page
export const Side = () =>
<aside className="main-sidebar sidebar-dark-primary elevation-4">
<a href="#" className="brand-link">
<span className="brand-text font-weight-light">Dashboard</span>
</a>
<div className="sidebar">
<div className="user-panel mt-3 pb-3 mb-3 d-flex">
<div className="image"></div>
<div className="info">
Irtaza
</div>
</div>
<nav className="mt-2">
<li><Link to='/'>Home</Link></li>
<li><Link to='/posts'><Fausers /> Posts </Link></li>
<li><Link to='/user-lists'><Fafile/> Users </Link></li>
<li><Link to='*'><Fatimes/> Whoops 404 </Link></li>
</nav>
</div>
</aside>
And finally this is my table.js page
export default class MyTable extends React.Component {
constructor(props) {
super(props);
this.state = {
currentPage: this.props.pgNo,
details : [],
id: null
}
this.MaxPages = 0;
}
PrevButton() {
if (this.state.currentPage === 0) {
return (null);
} else {
return (
<button
type="button"
key={this.state.currentPage}
style={{
float: "left"
}}
onClick=
{ () => { this.setState({ currentPage: this.state.currentPage - 1 }) } }>
Previous Page
</button>
);
}
}
NextButton() {
if (this.state.currentPage === this.MaxPages - 1) {
return (null);
} else {
return (
<button
style={{
float: "right"
}}
key={this.props.pgNo}
onClick={() => {
this.setState({
currentPage: this.state.currentPage + 1
})
}}>
Next Page
</button >
);
}
}
createTable = () => {
let tableHeader = <thead>
<tr>
{this.props.columns.map(column => {
return <th key={column.name}>
{column.name}
</th>
})}
</tr>
</thead>;
this.state.number = this.state.number + 1;
let tableRows = [];
for (let i = this.state.currentPage * this.props.maxItems; (i < (this.state.currentPage + 1) * this.props.maxItems) && (i <= this.props.data.length); i++) {
this.state.id= i + 1;
let row = <Link to={{
pathname: `/user-lists/details(/${i+1})`
}}>
<tr key={i}>
{this
.props
.columns
.map(column => {
this.state.id= i + 1;
return (
<td key={column.key}>
{this.props.data[i][column.key]}
</td>
)
})}
</tr>
</Link>
tableRows.push(row)
}
for (let i = 0; i <= Math.ceil(this.props.data.length / this.props.maxItems); i++) {
this.MaxPages = i;
}
let tableBody = <tbody>{tableRows}</tbody>;
return <table>{tableHeader}{tableBody}
</table>;
}
render() {
return (
<div className="col-md-6">
<div className="container-fluid">
<div
className="table table-bordered"
style={{
marginLeft: "70%",
marginRight: "5%"
}}>
{this.createTable()}
{this.PrevButton()}
{this.NextButton()}
</div>
</div>
</div>
)
}
}
Every time I click on a Link in sidebar.js
it redirects me to the new link but does not render anything also it gives me an error "Failed to load resource: the server responded with a status of 404 (Not Found)"
I dont know what i am doing wrong. Feel free to point out any mistakes you see.

Firstly, In order for Link to work correctly, it needs to Receive the Router Props but since its rendered as a Route, it doesn't receive any props.
Secondly all routes are defined as children to Side, but they are never rendered in the Side component
You would write your components like
App.js
class App extends Component {
render () {
return (
<div>
<BrowserRouter>
<div>
<Route component={Side}>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/user-lists" component={Table}>
<Route path="*" component={NotFound}/>
</Switch>
</div>
</BrowserRouter>
</div>
)
}
}
export default App
and Side.js
export const Side = (props) => (
<aside className="main-sidebar sidebar-dark-primary elevation-4">
<a href="#" className="brand-link">
<span className="brand-text font-weight-light">Dashboard</span>
</a>
<div className="sidebar">
<div className="user-panel mt-3 pb-3 mb-3 d-flex">
<div className="image"></div>
<div className="info">
Irtaza
</div>
</div>
<nav className="mt-2">
<li><Link to='/'>Home</Link></li>
<li><Link to='/posts'><Fausers /> Posts </Link></li>
<li><Link to='/user-lists'><Fafile/> Users </Link></li>
<li><Link to='*'><Fatimes/> Whoops 404 </Link></li>
</nav>
</div>
</aside>
)

Related

match.params or useParams() not rendering component even when URL contains params

Description
It's normally when user get details for item like blog post, movie, or shop item. the URL with params '/item/123' will render component with information for specific item.
Problem
I'm stuck in two days, think about what's happening. trying to solve this problem with different approach but the result stays the same.
let me show you how i'm making routes for entire apps :
{
path: '/konfigurasi',
sidebar: null,
exact: true,
main: () => (
<>
<PengaturanPengguna/>
</>
)
},
{
path: '/konfigurasi/pengaturan-pengguna',
sidebar: null,
exact: true,
main: () => (
<>
<PengaturanPengguna/>
</>
)
},
{
path: '/konfigurasi/grup-dan-hak-akses-pengguna',
sidebar: null,
exact: true,
main: () => (
<>
<GrupDanHakAkses/>
</>
)
},
{
path: '/konfigurasi/grup-dan-hak-akses-pengguna/form-kontrol-dan-hak-akses/:groupId/edit',
sidebar: null,
main: () => (
<>
<FormKontrolDanHakAksesEdit/>
</>
)
},
The problem is the route with params :
{
path: '/konfigurasi/grup-dan-hak-akses-pengguna/form-kontrol-dan-hak-akses/:groupId/edit',
sidebar: null,
main: () => (
<>
<FormKontrolDanHakAksesEdit/>
</>
)
},
the URL changes :groupId correctly, but it doesn't render anything and i can't see the error even it's happen.
here's how i'm render the routes :
App.js
const App = () => {
return (
<Wrapper>
<SidebarApp />
<Switch>
{routes
.filter(({ sidebar }) => !!sidebar)
.map((route, index) => (
<Route key={index} path={route.path} exact={route.exact} />
))}
</Switch>
<ContentWrapper>
<Content>
<NavigationBar />
<BreadCrumbs/>
<TextHeader/>
<Switch>
{routes
.filter(({ main }) => !!main)
.map((route, index) => (
<Route
key={index}
path={route.path}
exact={route.exact}
children={<route.main />}
/>
))}
</Switch>
</Content>
<Footer />
</ContentWrapper>
</Wrapper>
);
};
And, index.js
// make a private route
const PrivateRoute = ({ component: Component, ...rest }) => {
const { loading, error, data } = useQuery(IS_LOGGED_IN);
if (loading) return <p>Loading...</p>
if (error) return <p>Error!</p>
return (
<Route
{ ...rest }
render={ props => data.isLoggedIn === true ? (
<Component {...props}/>
) : (
<Redirect
to = {{
pathname: '/login',
state: { from: props.location }
}} />
)
}
/>
)
}
// mapping route
const routing = (
<ApolloProvider client={client}>
<Router>
<Switch>
<Route path='/login' component={Login} />
<Route path='/reset-password' component={ResetPassword} />
<Route exact path='/new-password/:token' component={NewPassword} />
<PrivateRoute path='/' component={App} />
</Switch>
</Router>
</ApolloProvider>
)
ReactDOM.render(routing, document.getElementById('root'));
Code
The structure of code is just like this :
/Component
/Table
index.js
/Form
index.js
/pages
/grupDanHakAksesPengguna
formKontrolDanHakAksesPenggunaEdit
grupDanHakAkses
/Component/Table
const TableGroup = ({ data, loading, error }) => {
const usersGroup = data?.getAllUserGroup.rows;
if(loading) return <p>Loading...</p>
if(error) return <p>Error...</p>
return (
<Table>
<thead>
<tr>
<th>NO</th>
<th>Nama Role Akun</th>
<th>Deskripsi Role</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
{
usersGroup.map((usergroup, index) => (
<tr key={ usergroup.groupId }>
<td>{ index + 1 }</td>
<td> { usergroup.groupName } </td>
<td> { usergroup.groupDesc } </td>
<td>
<div className='d-flex gap-2'>
<Link to={`/konfigurasi/grup-dan-hak-akses-pengguna/form-kontrol-dan-hak-akses-pengguna/${usergroup.groupId}/edit`}
> <Button
variant='outline-info'
>Edit</Button></Link>
<Button variant='outline-danger'>Hapus</Button>
</div>
</td>
</tr>
))
}
</tbody>
</Table>
)
}
/pages/grupDanHakAksesPengguna/grupDanHakAkses
const GrupDanHakAkses = () => {
const { data, refetch, loading, error } = useQuery(GET_ALL_USER_GROUP);
return (
<Container>
<Card>
<Card.Title>
<p className='text-bold base-md mx-3 mt-3 mb-3 text-uppercase'>data grup akun</p>
<div className='d-flex justify-content-between'>
<Form inline>
<Search/>
<Button variant="secondary">Muat Ulang</Button>
</Form>
<div>
<Button variant="primary"> <Link to="/konfigurasi/grup-dan-hak-akses-pengguna/form-kontrol-dan-hak-akses" className='text-white'>Grup Akun</Link> </Button>
</div>
</div>
</Card.Title>
<Card.Body>
<TableGroup data={ data } loading={ loading } error={ error } />
</Card.Body>
</Card>
</Container>
)
};
/Component/Form
const FormKH = ({ defaultValues }) => {
const {
register,
handleSubmit,
reset,
setValue,
formState: { errors: formErrors },
} = useForm({
defaultValues: useMemo(() => defaultValues, [defaultValues]),
});
useEffect(() => {
console.log(defaultValues);
},[defaultValues])
return (
<Form>
<Form.Group className="mb-3">
<label className="text-bold base-sm">Kode Grup</label>
<Form.Control type="text" name="groupId" {...register('groupId', {
required: true,
})} />
</Form.Group>
<Form.Group className="mb-3">
<label className="text-bold base-sm">Nama Grup</label>
<Form.Control type="text" name="groupName" {...register('groupName', {
required: true
})} />
</Form.Group>
<Form.Group className="mb-3">
<label className="text-bold base-sm">Deskripsi Grup</label>
<Form.Control as="textarea" name="groupDesc" {...register('groupDesc', {
required: true,
})}/>
</Form.Group>
</Form>
)
};
/pages/grupDanHakAksesPengguna/formKontroldanHakAksesPenggunaEdit
const FormKontrolDanHakAkses = (props) => {
const { groupId } = useParams();
const [detailGroup, { data, loading }] = useLazyQuery(GET_SINGLE_USER_GROUP)
useEffect(() => {
detailGroup({
variables: {
groupId: groupId
}
})
}, [groupId])
// const { data, loading, error } = useQuery(GET_SINGLE_USER_GROUP, {
// variables: {
// groupId
// },
// onError: (err) => {
// console.error(JSON.stringify(err, null, 2));
// }
// });
const handleSubmit = (data) => {
// disini letakkan ternary operator untuk membedakan antara create dan edit
}
return (
<Container>
<Row>
<Col sm={6}>
<Card>
<Card.Header>
<Card.Title>
<span className='text-uppercase'>Informasi Grup Akun</span>
</Card.Title>
</Card.Header>
<Card.Body>
<FormKH defaultValues={ data?.getSingleUserGroup }/>
</Card.Body>
</Card>
</Col>
</Row>
</Container>
)
};
Spec
React v16
React Router v5.3.0
Question
What's the best approach for fixing this problem so if the URL params changes also showing the component too ?
Any help will be appreciated, thanks in advance.

How can I use react-router after catching an error with an error boundary?

I have a simple react application (created with create-react-app) that uses react-router to navigate between components that are the "body" of my app. I recently started looking into error handling and came across error boundaries. When a user experiences an error, I would like to still show the shell of the application (for navigational use) with the body replaced by an error message. Currently I have the following code:
index.js:
...
ReactDOM.render(
<Provider store={store}>
<Router>
<App />
</Router>
</Provider>,
document.getElementById('root')
);
App.js:
...
function App() {
return (
<div className="container">
<Header />
<ErrorBoundary>
<Switch>
<Route exact path="/" component={UnderConstruction} />
<Route path="/pas" component={PAS} />
<Route path="/github/:user" component={GithubPage} />
<Route path="/reddit" component={RedditPage} />
</Switch>
</ErrorBoundary>
</div>
);
}
Header.js:
const links = [
{ link: '/', linkText: 'Home' },
{ link: '/pas', linkText: 'PAS' },
{ link: '/github', linkText: 'Github' },
{ link: '/reddit', linkText: 'Reddit' }
];
const Header = () => {
const activeStyle = { color: '#f15b2a' };
return (
<nav className="row text-center mb-3">
<div className="col-12">
{links.map(({ link, linkText }) => (
<NavLink
to={link}
key={link}
exact
activeStyle={activeStyle}
className="btn btn-dark">
{linkText}
</NavLink>
))}
</div>
</nav>
);
};
ErrorBoundary.js:
...
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { error: null, eventID: null };
}
componentDidCatch(error, errorInfo) {
this.setState({ error });
Sentry.withScope(scope => {
scope.setExtras(errorInfo);
const eventID = Sentry.captureException(error);
this.setState({ eventId: eventID });
});
}
handleReportClick = e => {
e.preventDefault();
Sentry.showReportDialog({ eventId: this.state.eventId });
};
render() {
if (this.state.error) {
//Render fallback UI
return <ErrorPage onReportClick={this.handleReportClick} />;
} else {
//When there's not an error, render the children untouched
return this.props.children;
}
}
}
ErrorPage.js:
...
const ErrorPage = ({ onReportClick, ...props }) => {
return (
<>
<div className="row mb-3">
<div className="col-12 text-center">
<h1 className="display-4">
An error has been encountered.
</h1>
</div>
</div>
<div className="row">
<div className="col-6 text-center">
<button
className="btn btn-lg btn-outline-info"
onClick={onReportClick}>
Report feedback
</button>
</div>
<div className="col-6 text-center">
<a
href={process.env.REACT_APP_ENVIRONMENT_URL}
className="btn btn-lg btn-outline-info">
Go back to the worklist
</a>
</div>
</div>
</>
);
};
Since <Header /> is outside the error boundary in App.js, I'm expecting it to show up and provide the ability to navigate away from the error page. The header shows up on the error page and clicking through the nav links changes the URL; however, the application doesn't actually go to any of the other pages, it just sits on the error page.
You can wrap Header in withRouter HOC. This way, the component is "aware" of react-router. This way, Link component from react-router should behave as expected.

How do I pass data from a clicked component from one Route to another in React?

I'm working on a video game search app which fetches data from the GiantBomb API using React Router. When a search is done on the SearchGames component, it returns a list of games from the API through the Game component. What I'm stuck on is figuring out how to pass data details of a listed game you click to the GamesDetail component using Router. I don't think I can do this using props since a separate view with all the details is neither a parent or a child component. I hope what I'm asking makes sense.
class App extends Component {
render() {
return (
<Router>
<div className="App">
<Nav />
<div className="container">
<Switch>
<Route exact path="/" component={MainPage} />
<Route exact path="/games" component={GameSearch} />
<Route exact path="/about" component={About} />}
<Route exact path="/details" component={GamesDetails} />}
</Switch>
</div>
</div>
</Router>
);
}
}
class Search extends Component {
constructor(props) {
super(props);
this.state = {
title: "",
games: []
}
}
updateInput = (event) => {
this.setState({
title: event.target.value
});
}
handleGames = (search) => {
const proxyUrl = "https://cors-anywhere.herokuapp.com/";
const key = "8cd10a7136710c1003c8e216d85941ace5a1f00e";
const endpoint = `https://www.giantbomb.com/api/search/?api_key=`;
const url = proxyUrl + endpoint + key + `&format=json&resources=game&query=${search}&limit=30`;
fetch(url)
.then(res => res.json())
.then(data => {
const response = data.results;
console.log(response);
response.forEach(game => {
this.setState(prevState => ({
games: prevState.games.concat(game)
}))
});
});
this.setState({
games: []
})
}
handleSubmit = (e) => {
const { title } = this.state;
e.preventDefault();
if (!title) {
return;
} else {
this.handleGames(title);
}
}
render() {
const { games } = this.state;
return (
<div className="App">
<div className="search-bar">
<form>
<input
className="input-field"
type="text"
placeholder="Search Game"
onChange={this.updateInput}
/>
<button
className="search-button"
onClick={this.handleSubmit}
>Search</button>
</form>
</div>
<div className="container">
{games.length > 0 ? (
games.map(game => {
return <Game
key={game.id}
icon={game.image.icon_url}
gameTitle={game.name}
/>
})
) : (
console.log(this.state.title)
)
}
</div>
</div>
);
}
}
const Game = (props) => {
const { icon, gameTitle } = props;
return (
<div className="games-container">
<div className="game-box">
<img src={icon} alt="icon" />
<Link to="/details">
<p><strong>{gameTitle}</strong></p>
</Link>
</div>
</div>
);
}
const GameDetails = (props) => {
const { icon, release, genres, summary} = props;
return (
<div className="details-content">
<div className="box-art">
<img src={icon} alt="box art" />
</div>
<div className="game-info">
<h1>Game Details</h1>
<div className="release-date">
<h3>Release Data</h3>
<p>{release}</p>
</div>
<div className="genres">
<h3>Genres</h3>
<p>{.genres}</p>
</div>
<div className="summary">
<h3>Summary</h3>
<p>{summary}</p>
</div>
</div>
</div>
);
}
You should be able to achieve this using Link with to as an object, which exposes a state than can be passed to the resulting Route:
// ...
// pass game object to Game component
// if you are passing game, you probably don't need other props explicitly passed
{games.length > 0 ? (
games.map(game => {
return <Game
key={game.id}
game={game}
icon={game.image.icon_url}
gameTitle={game.name}
/>
})
) : (
console.log(this.state.title)
)
}
// ...
const Game = (props) => {
const { icon, gameTitle, game } = props;
return (
<div className="games-container">
<div className="game-box">
<img src={icon} alt="icon" />
<Link to={{ pathname: "/details", state: { game } }}>
<p><strong>{gameTitle}</strong></p>
</Link>
</div>
</div>
);
}
You can then access this state from prop location that react-router-dom injects into props:
const GamesDetails = (props) => {
const { image: { icon_url: icon }, release, genres, summary } = props.location.state.game;
return (
<div className="details-content">
<div className="game-info">
<h1>Game Details</h1>
<div className="box-art">
<img src={icon} alt="box art" />
</div>
<div className="release-date">
<h3>Release Data</h3>
<p>{release}</p>
</div>
<div className="genres">
<h3>Genres</h3>
<p>{genres}</p>
</div>
<div className="summary">
<h3>Summary</h3>
<p>{summary}</p>
</div>
</div>
</div>
);
}
Here is an example in action.
Hopefully that helps!

How to filter the props for to render with ReactJS?

I have a code that get data of a json-server. And to render seven names in the screen. I want to filter by input and to render only the elements filtereds.
My Apps.js:
class AppRouter extends React.Component {
state = {
employeeCurrent: [],
employee: []
};
componentDidMount() {
axios
.get("http://127.0.0.1:3004/employee")
.
then(response => this.setState({ employee: response.data }));
}
add = name => {
this.setState(prevState => {
const copy = prevState.employeeCurrent.slice();
copy.push(name);
return {
employeeCurrent: copy
};
});
};
render() {
return (
<Router>
<div className="router">
<Route
exact
path="/"
render={props => (
<Home
{...props}
add={this.add}
employee={this.state.employee}
currentEmployee={this.state.currentEmployee}
/>
)}
/>
<Route
path="/user/:id"
component={props => (
<User
{...props}
employee={this.state.employee}
currentEmployee={this.state.currentEmployee}
/>
)}
/>
</div>
</Router>
);
}
}
My body.js (Where has the function for to render)
class Body extends React.Component {
getName = () => {
const { employee, add } = this.props;
return employee.map(name => (
<Link className="link" to={`/user/${name.name}`}>
{" "}
<div onClick={() => add(name)} key={name.id} className="item">
{" "}
<img
className="img"
src={`https://picsum.photos/${name.name}`}
/>{" "}
<h1 className="name"> {name.name} </h1>
</div>{" "}
</Link>
));
};
render() {
return <div className="body">{this.getName()}</div>;
}
}
I tried to pass the state to the App. JS but had no success. I tried everything in the Body. JS but also not succeeded. Could someone help me how to do this?
I'm on the phone so there are some things that are bad to indent. Sorry!
Try this,
class Body extends React.Component {
getName = () => {
const { employee, add } = this.props;
//Filter names in employee array with input
const filterNames = employee.filter(x => x.name === "check with the input value" );
// Then map over the filtered names
return filterNames.map(name => (
<Link className="link" to={`/user/${name.name}`}>
{" "}
<div onClick={() => add(name)} key={name.id} className="item">
{" "}
<img
className="img"
src={`https://picsum.photos/${name.name}`}
/>{" "}
<h1 className="name"> {name.name} </h1>
</div>{" "}
</Link>
));
};
render() {
return <div className="body">{this.getName()}</div>;
}
}

React Router v4 Modal

I know how to do it in previous versions of React Router but I absolutely do not know how to do it in new React Router v4. Can somebody help me?
What do I want?
When you type /image/1 in browser url, page will appear as normal.
When you click on <Link>Image as modal</Link> with state modal: true, modal with image will appear BUT behind modal must be the previous content + url in browser === /image/1... Then If you press F5, page will appear as normal.
Example: instagram... etc
What do I think I'm doing wrong?
I do not know how to display the previous content. That is all I guess.
Code:
const Images = (props) => {
return (
<div>
<h2>Images</h2>
<ul>
<li><Link to={{
pathname: '/image/1',
state: {
modal: true
}
}}>Image as modal</Link></li>
<li><Link to="/image/2">Image</Link></li>
</ul>
</div>
)
}
const Image = (props) => {
return (
<div>
<h2>Image {props.match.params.id}</h2>
<ul>
<li><Link to="/images">Back to Images</Link></li>
</ul>
</div>
)
}
ReactDOM.render(
<Provider store={store}>
<Router>
<div>
<Route path='/images' component={Images} />
<Route path='/image/:id' component={Image} />
</div>
</Router>
</Provider>,
document.getElementById('digital')
)
I had the same problem, so I created:
http://npmjs.com/package/react-router-modal
It allows you to attach modals to routes. You would use it as follows:
import { ModalContainer, ModalRoute } from 'react-router-modal';
// ...
ReactDOM.render(
<Provider store={store}>
<Router>
<div>
<Route path='/images' component={Images} />
<ModalRoute path='/image/:id' component={Image} />
</div>
</Router>
<ModalContainer />
</Provider>,
document.getElementById('digital')
)
There are a few simple examples at https://davidmfoley.github.io/react-router-modal-examples/
Hope it helps.
I was able to use the solution described here, which refers to this code example:
import React from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
// This example shows how to render two different screens
// (or the same screen in a different context) at the same url,
// depending on how you got there.
//
// Click the colors and see them full screen, then "visit the
// gallery" and click on the colors. Note the URL and the component
// are the same as before but now we see them inside a modal
// on top of the old screen.
class ModalSwitch extends React.Component {
// We can pass a location to <Switch/> that will tell it to
// ignore the router's current location and use the location
// prop instead.
//
// We can also use "location state" to tell the app the user
// wants to go to `/img/2` in a modal, rather than as the
// main page, keeping the gallery visible behind it.
//
// Normally, `/img/2` wouldn't match the gallery at `/`.
// So, to get both screens to render, we can save the old
// location and pass it to Switch, so it will think the location
// is still `/` even though its `/img/2`.
previousLocation = this.props.location;
componentWillUpdate(nextProps) {
let { location } = this.props;
// set previousLocation if props.location is not modal
if (
nextProps.history.action !== "POP" &&
(!location.state || !location.state.modal)
) {
this.previousLocation = this.props.location;
}
}
render() {
let { location } = this.props;
let isModal = !!(
location.state &&
location.state.modal &&
this.previousLocation !== location
); // not initial render
return (
<div>
<Switch location={isModal ? this.previousLocation : location}>
<Route exact path="/" component={Home} />
<Route path="/gallery" component={Gallery} />
<Route path="/img/:id" component={ImageView} />
</Switch>
{isModal ? <Route path="/img/:id" component={Modal} /> : null}
</div>
);
}
}
const IMAGES = [
{ id: 0, title: "Dark Orchid", color: "DarkOrchid" },
{ id: 1, title: "Lime Green", color: "LimeGreen" },
{ id: 2, title: "Tomato", color: "Tomato" },
{ id: 3, title: "Seven Ate Nine", color: "#789" },
{ id: 4, title: "Crimson", color: "Crimson" }
];
function Thumbnail({ color }) {
return (
<div
style={{
width: 50,
height: 50,
background: color
}}
/>
);
}
function Image({ color }) {
return (
<div
style={{
width: "100%",
height: 400,
background: color
}}
/>
);
}
function Home() {
return (
<div>
<Link to="/gallery">Visit the Gallery</Link>
<h2>Featured Images</h2>
<ul>
<li>
<Link to="/img/2">Tomato</Link>
</li>
<li>
<Link to="/img/4">Crimson</Link>
</li>
</ul>
</div>
);
}
function Gallery() {
return (
<div>
{IMAGES.map(i => (
<Link
key={i.id}
to={{
pathname: `/img/${i.id}`,
// this is the trick!
state: { modal: true }
}}
>
<Thumbnail color={i.color} />
<p>{i.title}</p>
</Link>
))}
</div>
);
}
function ImageView({ match }) {
let image = IMAGES[parseInt(match.params.id, 10)];
if (!image) return <div>Image not found</div>;
return (
<div>
<h1>{image.title}</h1>
<Image color={image.color} />
</div>
);
}
function Modal({ match, history }) {
let image = IMAGES[parseInt(match.params.id, 10)];
if (!image) return null;
let back = e => {
e.stopPropagation();
history.goBack();
};
return (
<div
onClick={back}
style={{
position: "absolute",
top: 0,
left: 0,
bottom: 0,
right: 0,
background: "rgba(0, 0, 0, 0.15)"
}}
>
<div
className="modal"
style={{
position: "absolute",
background: "#fff",
top: 25,
left: "10%",
right: "10%",
padding: 15,
border: "2px solid #444"
}}
>
<h1>{image.title}</h1>
<Image color={image.color} />
<button type="button" onClick={back}>
Close
</button>
</div>
</div>
);
}
function ModalGallery() {
return (
<Router>
<Route component={ModalSwitch} />
</Router>
);
}
export default ModalGallery;
a simple modal route example with javascript that can use with any routing system
<button onClick={() => {
this.setState({ modal: true });
window.history.pushState("","","/gallery/image/img_1233")
}}>
Open Modal
</button>
//Link Button
<Link href="/gallery/image/img_1233">
<a>Open Page</a>
</Link>
complete example here: https://github.com/mohammad-amin-hesam/react-modal-route-example

Categories

Resources