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
Related
I have created react app. I have directory components with folder pages where I have FindAllMetadata component in which I am making GET request and getting all metadata from API. I am passing loadedMetadata to AllMetadataTable as props. AllMetadataTable component is located in other folder called data. There I am displaying some information about fetched metadata items in table ( Creator, Time Created, Format ) and other fetched properties I am not displaying. In a table aside of the every fetched metadata I have + button which when clicked makes link ( route ) to the new page where I want display all information about fetched metadata, single clicked metadata item. Details component is located in folder pages. I want to display clicked Metadata from AllMetadataTable in Details page or in Metadata component.
Here is my App.js :
import React, { Suspense } from 'react';
import { Switch, Route, BrowserRouter } from 'react-router-dom';
import classes from './App.module.css'
import LoadingSpinner from './components/UI/LoadingSpinner';
import Layout from './components/layout/Layout';
import Footer from './components/layout/Footer';
import RequestMenu from './components/UI/RequestMenu';
// load components only when user gets to them
const Dashboard = React.lazy(() => import('./components/pages/Dashboard'));
const NewData = React.lazy(() => import('./components/pages/NewData'));
const NotFound = React.lazy(() => import('./components/pages/NotFound'));
const FindAllReceivedRequests = React.lazy(() => import('./components/pages/FindAllReceivedRequests'));
const FindAllGivenConsents = React.lazy(() => import('./components/pages/FindAllGivenConsents'));
const ReadConsent = React.lazy(() => import('./components/pages/ReadConsent'));
const FindData = React.lazy(() => import('./components/pages/FindData'));
const FindAllMetadata = React.lazy(() => import ('./components/pages/FindAllMetadata'));
const NewPartnerRequest = React.lazy(() => import('./components/pages/NewPartnerRequest'));
const FindAllGivenRequests = React.lazy(() => import('./components/pages/FindAllGivenRequests'));
const FindAllReceivedConsents = React.lazy(() => import('./components/pages/FindAllReceivedConsents'));
const Metadata = React.lazy(() => import('./components/data/Metadata'));
function App() {
return (
<BrowserRouter>
<Layout>
<Suspense fallback= { <div className = { classes.centered }> <LoadingSpinner /> </div> } >
<Switch>
<Route path ='/' exact>
<Dashboard />
<FindData />
</Route>
<Route path= '/new-data' exact>
<NewData />
</Route>
<Route path= '/metadata' exact>
<FindAllMetadata />
</Route>
<Route path = '/data'>
<Metadata />
</Route>
<Route path= '/request' exact>
<RequestMenu />
<FindAllReceivedRequests />
<section style = {{ marginTop: '5rem',
}}>
<FindAllGivenConsents />
</section>
</Route>
<Route path= '/givenrequest' exact>
<RequestMenu />
<FindAllGivenRequests />
<section style = {{ marginTop: '5rem',
}}>
<FindAllReceivedConsents />
</section>
</Route>
<Route path= '/transfer-data' exact>
<ReadConsent />
</Route>
<Route path= '/partner-request' exact>
<NewPartnerRequest />
</Route>
<Route path= '*'>
<NotFound />
</Route>
</Switch>
</Suspense>
</Layout>
<Footer />
</BrowserRouter>
);
}
export default App;
Here is my FindAllMetadata.js where I am fetching allMetadata ( it is working ):
import React, { useState, useEffect, useMemo } from 'react';
import AllMetadataTable from '../data/AllMetadataTable';
import LoadingSpinner from '../UI/LoadingSpinner';
import styles from '../UI/Messages.module.css';
import styled from '../style/Form.module.css';
import { readAllMetadata } from '../lib/api';
const FindAllMetadata = () => {
const [allMetadata, setAllMetadata] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const [enteredMetadataFilter, setEnteredMetadataFilter] = useState('all');
// When page is loaded immediately fetch (and display) all metadata (only running the effect when enteredMetadataFilter changes)
useEffect(() => {
// fetch metadata by entered request filter (all or my)
const readAllMetadataHandler = async () => {
setIsLoading(true);
setError(null);
try {
const loadedAllMetadata = await readAllMetadata(enteredMetadataFilter);
setAllMetadata(loadedAllMetadata);
} catch (error) {
setError(error);
}
setIsLoading(false);
};
readAllMetadataHandler();
}, [enteredMetadataFilter]);
// display fetched content
const content = useMemo(() => {
if (error) {
return <div className={styles.negative}> { error.message } </div>;
} else if (isLoading) {
return <section style = {{margin : '1rem 17rem' }} ><LoadingSpinner /> </section>;
} else {
return (
<AllMetadataTable allMetadata = { allMetadata }
metadataFilter = { enteredMetadataFilter }
issLoading = { isLoading }
/>
);
}
}, [isLoading, error, allMetadata, enteredMetadataFilter]);
return (
<>
{/** pick filter for displaying metadata */}
{!isLoading &&
<section style= {{ marginLeft : '-10rem'}} >
<select className={styled.selectControl}
onChange={ event => {
setEnteredMetadataFilter(event.target.value);
}} >
<option value='' disabled style={{ color: '#cccccc' }} > Choose an option
</option>
<option value = 'all'> All Metadata </option>
<option value = 'my'> My Data </option>
</select>
</section>
}
<section>
{/**display content by status: error, loading, allmetadata, mymetadata */}
{ content }
</section>
</>
)
}
export default FindAllMetadata;
Here is my AllMetadataTable.js component where I am displaying fetched metadata in a table ( it is working and when I click + button it is redirecting me to correct URL ) :
import React from 'react';
import { Table, Button } from 'semantic-ui-react';
import "semantic-ui-css/components/table.min.css";
//import Metadata from './Metadata';
import classes from '../style/Form.module.css';
import Time from '../time/time';
import { useHistory } from 'react-router-dom';
import Metadata from './Metadata';
const AllMetadataTable = ({ allMetadata, metadataFilter, issLoading }) => {
const history = useHistory();
// sorted by time created - newest first
const allMetadataSorted = [...allMetadata].sort((a, b) => {
return new Date(b.TimestampCreated) - new Date(a.TimestampCreated);
});
// open details page for wanted metadata
const openDetailsPage = (key) => {
history.push({
pathname: 'data',
search: `?id=${allMetadata[key].DObjectId}`
})
};
return (
<>
{!issLoading &&
<Table celled fixed singleLine
style={{
width : '60rem',
marginLeft: '-10rem',
}} >
<Table.Header>
<Table.Row>
<Table.HeaderCell>Creator</Table.HeaderCell>
<Table.HeaderCell>Host</Table.HeaderCell>
<Table.HeaderCell>Domain</Table.HeaderCell>
<Table.HeaderCell>Format</Table.HeaderCell>
<Table.HeaderCell>Time Created</Table.HeaderCell>
<Table.HeaderCell
style={{
width : '4rem',
}}>Details</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{allMetadataSorted.map((metadata) => (
<React.Fragment key={metadata.key}>
<Table.Row>
<Table.Cell>{metadata.OrgIdCreator}</Table.Cell>
<Table.Cell>{metadata.OrgIdHost}</Table.Cell>
<Table.Cell>{metadata.TagsDomain}</Table.Cell>
<Table.Cell>{metadata.DataFormatId}</Table.Cell>
<Table.Cell>{Time(metadata.TimestampCreated)}</Table.Cell>
<Table.Cell>
{/** open/close metadata */}
<Button className={classes.uichange}
style ={{
border: 'none',
borderRadius: '0px',
color: 'white',
cursor: 'pointer',
backgroundColor: '#19a47c',
margin: '0 1rem',
fontSize : 22 }}
onClick={() => openDetailsPage(metadata.key) }>
+
</Button>
</Table.Cell>
</Table.Row>
</React.Fragment>
))}
</Table.Body>
</Table>
}
</>
);
};
export default AllMetadataTable;`
Here is my Metadata.js component which I wanna show in a new page when + button in a table is clicked ( id I am getting is corrrect and it is displaying in a list correctly but all other fields are empty; how can I access other fields and display them ?) :
`import React from 'react';
import classes from '../style/SingleData.module.css';
import list from '../style/List.module.css';
import { Button } from 'semantic-ui-react';
//import styles from '../UI/Messages.module.css';
import LoadingSpinner from '../UI/LoadingSpinner';
import Time from '../time/time';
import { useLocation } from 'react-router-dom';
/** import ORG_NAME */
const ORG = process.env.REACT_APP_ORG_NAME;
const Metadata = (props) => {
const { search } = useLocation();
const id = new URLSearchParams(search).get('id');
console.log(id);
// close metadata
const stopReadingDataHandler = () => {
props.onClose()
};
return (
<>
<ul className={list.tableList} >
<li style={{ borderRadius: '0px' }} className={classes.data}>
<strong> Data Id: </strong> {id}
</li>
<li style={{ borderRadius: '0px' }} className={classes.data}>
<strong> Doc Type Code: </strong> {props.DocTypeCode}
</li>
<li style={{ borderRadius: '0px' }} className={classes.data}>
<strong> Data Format: </strong> {props.DataFormatId}
</li>
<li style={{ borderRadius: '0px' }} className={classes.data}>
<strong>Creator: </strong> {props.OrgIdCreator}
</li>
<li style={{ borderRadius: '0px' }} className={classes.data}>
<strong> Host: </strong> {props.OrgIdHost}
</li>
<li style={{ borderRadius: '0px' }} className={classes.data}>
<strong>Tags Content: </strong> {props.TagsContent}
</li>
<li style={{ borderRadius: '0px' }} className={classes.data}>
<strong>Domain: </strong> {props.TagsDomain}
</li>
<li style={{ borderRadius: '0px' }} className={classes.data}>
<strong>Time Created: </strong> {Time(props.TimestampCreated)}
</li>
<li style={{ borderRadius: '0px' }} className={classes.data}>
<strong>Time Updated: </strong> {Time(props.TimestampUpdated)}
</li>
{ /** display Cancel button if you are Creator or Host */}
{!props.isLoading && (props.OrgIdCreator === ORG || props.OrgIdHost === ORG) && props.transferCheckStatus === false ?
<div style={{ justifyContent: 'flex-start' }}
className={classes.Form__actions}>
<Button className={classes.uichangedelete}
style ={{
border: 'none',
borderRadius: '3px',
color: 'white',
cursor: 'pointer',
backgroundColor: 'red',
margin: '10px',
fontSize : 22 }}
type= 'button'
content='Cancel'
onClick={ stopReadingDataHandler }
/>
</div>
: null }
{/** display loading spinner if loading */}
{props.isLoading && <LoadingSpinner />}
</ul>
</>
);
};
export default Metadata;
I tried using props inside FindAllMetadata, inside AllMetadataTable; I created other page Details.js in same folder as FindAllMetadata ( pages folder ) ; I tried useHistory, useLocation, useParams etc. `
In your openDetailsPage function you are passing only the id to the Metadata component:
const openDetailsPage = (key) => {
history.push({
pathname: 'data',
search: ?id=${allMetadata[key].DObjectId}
})
};
You are using history.push method with the following parameters:
pathname => which is '/data/ for Metadata component
search => query to url (NOTE: Here you are passing only the id)
Try adding:
state => an object to pass to the Metadata components:
history.push({
pathname: 'data',
search: `?id=${allMetadata[key].DObjectId}`,
state: {item: allMetadata[key]}
})
Then access it on the Metadata component by using:
props.location.state.item
How can I use map and boolean variables inside those two paths and still return the attributes?
Those are the routes:
<Route exact path='/' component={()=>{return <Addroom/>}}/>
<Route exact path='/room' component={()=>{return <Room/>}}/>
Those are the the components that I want to return inside the routes:
{this.state.roomsList.map((element, key) => {
return (
<Room
id={key + 1}
key={key}
r={element.room}
rt={element.roomType}
ct={element.colorType}
sr={element.boxColor}
/>
);
})}
{this.state.isActive ? (
<Addroom add={this.create} />
) : (
<button style={{backgroundColor: "aquamarine", height: "20px", width: "60px", borderRadius: "5px", border: "2px"}} onClick={this.handleShow}>Create</button>
)}
Thank you! (:
If I understand correctly, you want to render the above second code snippet into one of two routes. You can merge the two implementations
Define anonymous inner functional components to pass to the Route.
Use the Route's render to render the anonymous inner functional component. This avoids remounting the component on every render cycle.
When using a Switch only the first route match is returned and rendered, and less specific paths will match before more specific paths, so switch the order to define the more specific paths before less specific paths. This removes the need to add the exact prop to every route.
Code:
import React, { Component } from "react";
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import 'bootstrap/dist/css/bootstrap.min.css'
import './App.css';
import Addroom from "./components/Addroom.js";
import Room from "./components/Room.js";
export default class App extends Component {
state = {
roomsList: [],
isActive: false
};
handleShow = () => {
this.setState({ isActive: true });
};
handleHide = () => {
this.setState({ isActive: false });
};
create = r => {
this.setState({
roomsList: [...this.state.roomsList, r],
isActive: false
});
};
render() {
const { isActive, roomsList } = this.state;
return (
<div
className="backGroundMain"
style={{
backgroundColor: "lightseagreen",
height: "600px",
width: "850px"
}}
>
<h1
style={{
backgroundColor: "aquamarine",
height: "40px",
width: "270px",
borderRadius: "5px",
border: "2px",
margin: "15px"
}}
>
My Smart House
</h1>
<Router>
<Switch>
<Route
path='/room'
render={()=> {
return roomsList.map((element, key) => (
<Room
id={key + 1}
key={key}
r={element.room}
rt={element.roomType}
ct={element.colorType}
sr={element.boxColor}
/>
))
}}
/>
<Route
path='/'
render={routeProps => isActive ? (
<Addroom add={this.create} {...routeProps}/>
) : (
<button
style={{
backgroundColor: "aquamarine",
height: "20px",
width: "60px",
borderRadius: "5px",
border: "2px"
}}
onClick={this.handleShow}
>
Create
</button>
)
}
/>
</Switch>
</Router>
</div>
);
}
}
So I have a custom component for my sidebar navigation that isn't completely functional. I'm trying to figure out how to dynamically add isSelected to the CustomItem component by location.
Edit: I'm assuming that useLocation from react-router-dom is my best bet for the first part.
Here's the code for the Dashboard:
/** #jsx jsx */
import React, { forwardRef } from 'react';
import {
Main,
Content,
LeftSidebar,
} from '#atlaskit/page-layout';
import {
Link,
Route,
Switch,
useParams,
useRouteMatch } from 'react-router-dom';
import { css, jsx } from '#emotion/core';
import { Section } from '#atlaskit/menu';
import {
Header,
NavigationHeader,
NestableNavigationContent,
SideNavigation,
CustomItem,
CustomItemComponentProps
} from '#atlaskit/side-navigation';
type CustomProps = CustomItemComponentProps & {
href: string;
};
const CustomLink = forwardRef<HTMLAnchorElement, CustomProps>(
(props: CustomProps, ref) => {
const { href, children, ...rest } = props;
return (
<Link to={href} ref={ref} {...rest}>
{children}
</Link>
);
},
);
function SideNavigationWrapper(props: { children: React.ReactNode }) {
return (
<div
css={css`
height: 100%;
& nav {
min-width: 20px;
overflow-x: hidden;
}
`}
>
{props.children}
</div>
);
}
function Components() {
let { component } = useParams();
if (component === 'assets') {
return (
<Assets />
);
}
if (component === 'orders') {
return (
<Orders />
);
}
return (
<div></div>
);
}
export default function Dashboard() {
let { path } = useRouteMatch();
return (
<Content>
<LeftSidebar
isFixed={true}
id="dash-navigation"
>
<SideNavigationWrapper>
<SideNavigationContent />
</SideNavigationWrapper>
</LeftSidebar>
<Main>
<div
style={{
marginLeft: 40,
marginRight: 40,
marginTop: 20,
}}
>
<Switch>
<Route exact path={path}>
<Overview />
</Route>
<Route path={`${path}/:component`}>
<Components />
</Route>
</Switch>
</div>
</Main>
</Content>
);
}
const SideNavigationContent = () => {
return (
<SideNavigation label="DashboardNav">
<NavigationHeader>
<Header>Dashboard</Header>
</NavigationHeader>
<NestableNavigationContent initialStack={[]}>
<Section>
<CustomItem
isSelected
component={CustomLink}
href="/dashboard"
iconBefore={<PortfolioIcon label="" />}
>
Portfolio
</CustomItem>
<CustomItem
component={CustomLink}
href="/dashboard/assets"
iconBefore={<SuitcaseIcon label="" />}
>
Assets
</CustomItem>
<CustomItem
component={CustomLink}
href="/dashboard/orders"
iconBefore={<RoadmapIcon label="" />}
>
Orders
</CustomItem>
</Section>
</NestableNavigationContent>
</SideNavigation>
);
};
It's also worth nothing that my approach has slightly messed up the styling as shown below:
Which should appear like this naturally without me having to hover over it.
Edit: I'm also assuming that I would have to override the styling with cssFn as per the documentation.
Any guidance on either two issues would be helpful. I've gave myself a headache trying to redo the entire apps routing. Figured I'd try my best to wrap this up tonight so I can move onto greater issues
You should assign boolean instead of putting props
const SideNavigationContent = props => {
//Add logic to control variable
//isPortfolioSelected, isAssetsSelect, isOrdersSelected, then assign to each item,
//Ex: isPortfolioSelected = props.selected['Portfolio']
return (
<SideNavigation label="DashboardNav">
<NavigationHeader>
<Header>Dashboard</Header>
</NavigationHeader>
<NestableNavigationContent initialStack={[]}>
<Section>
<CustomItem
isSelected={isPortfolioSelected}
component={CustomLink}
href="/dashboard"
iconBefore={<PortfolioIcon label="" />}
>
Portfolio
</CustomItem>
<CustomItem
isSelected={isAssetsSelect}
component={CustomLink}
href="/dashboard/assets"
iconBefore={<SuitcaseIcon label="" />}
>
Assets
</CustomItem>
<CustomItem
isSelecled={isOrdersSelected}
component={CustomLink}
href="/dashboard/orders"
iconBefore={<RoadmapIcon label="" />}
>
Orders
</CustomItem>
</Section>
</NestableNavigationContent>
</SideNavigation>
);
};
I can't display users data such as name when he is logged in. I have used props and state user as currentUser but i am unable to access these fields since the error says that it can't read property of undefined.
class UserPanel extends React.Component {
state = { user: this.props.currentUser }
dropdownOptions = () => [
{
key: "user",
text: (
<span>
Sign in as <strong>{this.state.user.displayName}</strong>
</span>
),
disabled: true
},
{
key: "avatar",
text: <span>Change Avatar</span>
},
{
key: "signout",
// Set a signout Function to enable user to sign out of the chat
text: <span onClick={event => this.handleSignOut(event)}>SignOut</span>
}
];
handleSignOut = (event) => {
// You need to prevent form submission. Use event.preventDefault() in your handle submit function.
event.preventDefault();
firebase
.auth()
.signOut()
.then(() => console.log("See you"));
}
render(){
console.log(this.props.currentUser);
return (
<Grid style={{ background: '#4c3c4c' }}>
<Grid.Column>
<Grid.Row style={{ padding: '1.2rem', margin: 0 }}>
<Header inverted floated='left' as='h2'>
<Icon name='code' />
<Header.Content>VirtualChat</Header.Content>
</Header>
</Grid.Row>
{/* User Dropdown Choices */}
<Header style={{ padding: "0.25em" }} as="h4" inverted>
<Dropdown
trigger={<span>{this.state.user.displayName}</span>}
options={this.dropdownOptions()}
/>
</Header>
</Grid.Column>
</Grid>
)
}
}
// index.js
const store = createStore(rootReducer, composeWithDevTools());
// change root component to a statefull component
class Root extends React.Component {
componentDidMount() {
firebase.auth().onAuthStateChanged(user => {
// If firebase has detect a user
if (user) {
// console.log(user);
this.props.setUser(user);
// We will redirect them to the home Route
this.props.history.push("/");
} else {
// In case user signout
this.props.history.push('/login');
this.props.clearUser();
}
});
}
render(){
return this.props.isLoading ? <Spinner /> : (
// All of our indivicuals routes will be nested in switch component which is nested to router component
<Switch>
{/* Root route of the app, we first set the path and then which component we watn */}
{/* We added exact keyword in order to secure that the main route will not match multiple components */}
<Route exact path="/" component={App} />
{/* Create routes for Login and Register */}
<Route path="/login" component={Login} />
<Route path="/register" component={Register} />
</Switch>
);
}
}
// To get loading data from our state object to see when user actions is loaded
const mapStateFromProps = state => ({
isLoading: state.user.isLoading
});
const RootWithAuth = withRouter(
connect(
// Using mapStateFromProps because, since state update are asynchronous and take some amount of time
mapStateFromProps,
{ setUser, clearUser }
)(Root)
);
// We render root because app is now our route
// In order to provide this global state/store to the other components we wrap the router in to a provider
// Provider will provide this global state to any component who want to make use of it
ReactDOM.render(
<Provider store={store}>
<Router>
<RootWithAuth />
</Router>
</Provider>,
document.getElementById('root')
);
registerServiceWorker();
I think that the value is undefined because you are not checking if the props has a value maybe the data that your are trying to render is not ready or is async. To handle this you can set your state in a componentDidMount so if the state.currentUser is null it means that the data isn't ready and you can render a loader or something similar.
class UserPanel extends React.Component {
state = { user: null }
dropdownOptions = () => [
{
key: "user",
text: (
<span>
Sign in as <strong>{this.state.user.displayName}</strong>
</span>
),
disabled: true
},
{
key: "avatar",
text: <span>Change Avatar</span>
},
{
key: "signout",
// Set a signout Function to enable user to sign out of the chat
text: <span onClick={event => this.handleSignOut(event)}>SignOut</span>
}
];
handleSignOut = (event) => {
// You need to prevent form submission. Use event.preventDefault() in your handle submit function.
event.preventDefault();
firebase
.auth()
.signOut()
.then(() => console.log("See you"));
}
componentDidMount(){
this.setState({ user: this.props.currentUser })
}
render(){
if( !this.state.user){
return <div>Curernt User doesnt exist!</div>
}
return (
<Grid style={{ background: '#4c3c4c' }}>
<Grid.Column>
<Grid.Row style={{ padding: '1.2rem', margin: 0 }}>
<Header inverted floated='left' as='h2'>
<Icon name='code' />
<Header.Content>VirtualChat</Header.Content>
</Header>
</Grid.Row>
{/* User Dropdown Choices */}
<Header style={{ padding: "0.25em" }} as="h4" inverted>
<Dropdown
trigger={<span>{this.state.user.displayName}</span>}
options={this.dropdownOptions()}
/>
</Header>
</Grid.Column>
</Grid>
)
}
}
You call this.props.state.user instead of this.state.user
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>
)