How implement a proper layout using reactjs and react-router - javascript

How can I implement a proper layout using reactjs and react-router.
Basically what I want to implement is something line the image below:
Note: I don't want implement the header & footer in the index.html.
So far what I have done and working is:
import React from 'react';
import { Router, Route, Link } from 'react-router'
class App extends React.Component {
render(){
return <div>
<h1>App</h1>
<ul>
<li><Link to="/home">Home</Link></li>
<li><Link to="/blog">Blog</Link></li>
<li><Link to="/portfolio">Portfolio</Link></li>
<li><Link to="/social">Social</Link></li>
<li><Link to="/about">about</Link></li>
</ul>
{this.props.children}
</div>
}
}
class Home extends React.Component {
render(){
return <p>home</p>
}
}
class Blog extends React.Component {
render(){
return <p>blog</p>
}
}
class Portfolio extends React.Component {
render(){
return <p>portfolio</p>
}
}
class Social extends React.Component {
render(){
return <p>social</p>
}
}
class About extends React.Component {
render(){
return <p>about</p>
}
}
let routes = <Router>
<Route path="/" component={App}>
<Route path="home" component={Home} />
<Route path="blog" component={Blog} />
<Route path="portfolio" component={Portfolio} />
<Route path="social" component={Social} />
<Route path="about" component={About} />
</Route>
</Router>
React.render(routes, document.body);
The above code it's working properly, but What I need is separate the the App in 3 components - > <Header />, content and <Footer />
something like:
class App extends React.Component {
render(){
return <Header />
{this.props.children}
<Footer />
}
}
class Footer extends React.Component {
render(){
return <div>Footer</div>
}
}
class Header extends React.Component {
render(){
return <div>
<h1>App</h1>
<ul>
<li><Link to="/home">Home</Link></li>
<li><Link to="/blog">Blog</Link></li>
<li><Link to="/portfolio">Portfolio</Link></li>
<li><Link to="/social">Social</Link></li>
<li><Link to="/about">about</Link></li>
</ul>
</div>
}
}
but when I implement it the routing doesn't work and I don't get any error, I think is something related with the {this.props.children}, so.. any idea how can I get it done?

Your render function transpiles to:
function render() {
return React.createElement(Header, null);
{
this.props.children;
}
React.createElement(Footer, null);
}
You're not seeing any errors because this is valid, but has unreachable code. It just returns a Header element.
You need to wrap its contents in another element, e.g.:
class App extends React.Component {
render() {
return <div>
<Header/>
{this.props.children}
<Footer/>
</div>
}
}
Edit: Imagine you wrote this code outside the context of React - what would you expect it to return?
function render() {
return 'Header';
'Content';
'Footer';
}
These are 3 separate statements, and since the first statement is a return, the last 2 are irrelevant, as they'll never be reached.
In order to return multiple objects from a function, you need to put them in a container of some sort, e.g. an Array:
function render() {
return [
'Header',
'Content',
'Footer'
]
}
However, you can't do this in a React component's render() method, as they must return either a React component, null, or false, hence you need to wrap contents with another element if you want to return multiple items.

Related

Redirecting to error page when path is invalid

I'm trying to redirect to an error page I created whenever the user puts an invalid path on my application. Let's say the user puts domain.com/efneroguqf or any random path.
Here's my App function inside App.js
function App() {
return (
<div className="App">
<BrowserRouter>
<Suspense fallback={(<div>Loading</div>)}>
<Navbar />
<Route exact path="/" component={Home} />
<Route path="/SignUp" component={SignUp} />
<PrivateRoute path="/Publish" component={Publish} />
<Route path="/AdminGenerator" component={AdminGenerator} />
<Route path="/AdminUsers" component={AdminUsers} />
<Route path="/List" component={List} />
<Route path="/publication" component={Details} />
<Route path="/error" component={ErrorBoundary} />
<PrivateRoute path="/MyPublications" component={MyPublications} />
<PrivateRoute path="/MyFavorites" component={MyFavorites} />
<PrivateRoute path="/MyInformation" component={MyInformation} />
<PrivateRoute path="/EditPublication" component={EditPublication} />
<Route exact path="*" component={Home} />
</Suspense>
</BrowserRouter>
</div>
);
}
Now I want to redirect to /error in case the path is invalid like I mentioned above. How can I achieve that?
Update: Here's my error page. I want to set the coding to 404 when this happens.
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
coding: props.location.state.coding
};
}
render(){
const { t } = this.props;
var codeMsg = t('errors.errorCode') + this.state.coding
return (
<div>
<div id="error-container">
<h1 id="error-title">{t('errors.errorTitle')}</h1>
<p id="error-status">{codeMsg}</p>
<p id="error-message">{t('errors.errorMessage')}</p>
</div>
<div id="link2-container">
{t('errors.errorBackHome')}
</div>
</div>
);
}
}
As the very last path, use a wildcard..
// last path in route list
<Route path="*" component={NotFoundPage} />
With 404 page:
const { Component } = React;
const { render } = ReactDOM;
const { Switch, Route, Link, HashRouter, withRouter } = ReactRouterDOM;
class Header extends Component {
render() {
return (
<div>
<ul>
<li><Link to='/'>Home</Link></li>
<li><Link to='/about'>About</Link></li>
<li><Link to='/contact'>Contact</Link></li>
<li><Link to='/iDontReallyExist'>Simulate Non-existent route</Link></li>
</ul>
</div>
);
}
}
class Main extends Component {
render() {
return (
<main>
<Switch>
<Route exact path='/' component={HomePage}/>
<Route path='/about' component={AboutPage}/>
<Route path='/contact' component={ContactPage}/>
<Route path='*' component={FourZeroFour}/>
</Switch>
</main>
);
}
}
class HomePage extends Component {
render() {
return (
<div>Home Page</div>
);
}
}
class AboutPage extends Component {
render() {
return (
<div>About Page</div>
);
}
}
class ContactPage extends Component {
render() {
return (
<div>Contact Page</div>
);
}
}
class SomeNonExistentRoute extends Component {
render() {
return (
<div>I dont exist</div>
);
}
}
class FourZeroFour extends Component {
render() {
return (
<div>Uh oh can't find that! 404!</div>
);
}
}
class Application extends Component {
render() {
let curRoute = this.props.location.pathname
return (
<div>
<Header />
<p>Current Route: {curRoute}</p>
<Main />
</div>
);
}
}
const App = withRouter(Application);
render((
<HashRouter>
<App />
</HashRouter>
), document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-router-dom/5.1.2/react-router-dom.min.js"></script>
With Home page as '404':
const { Component } = React;
const { render } = ReactDOM;
const { Switch, Route, Link, HashRouter, withRouter } = ReactRouterDOM;
class Header extends Component {
render() {
return (
<div>
<ul>
<li><Link to='/'>Home</Link></li>
<li><Link to='/about'>About</Link></li>
<li><Link to='/contact'>Contact</Link></li>
<li><Link to='/iDontReallyExist'>Simulate Non-existent route</Link></li>
</ul>
</div>
);
}
}
class Main extends Component {
render() {
return (
<main>
<Switch>
<Route exact path='/' component={HomePage}/>
<Route path='/about' component={AboutPage}/>
<Route path='/contact' component={ContactPage}/>
<Route path='*' component={HomePage}/>
</Switch>
</main>
);
}
}
class HomePage extends Component {
render() {
return (
<div>Home Page</div>
);
}
}
class AboutPage extends Component {
render() {
return (
<div>About Page</div>
);
}
}
class ContactPage extends Component {
render() {
return (
<div>Contact Page</div>
);
}
}
class SomeNonExistentRoute extends Component {
render() {
return (
<div>I dont exist</div>
);
}
}
class FourZeroFour extends Component {
render() {
return (
<div>Uh oh can't find that! 404!</div>
);
}
}
class Application extends Component {
render() {
let curRoute = this.props.location.pathname
return (
<div>
<Header />
<p>Current Route: {curRoute}</p>
<Main />
</div>
);
}
}
const App = withRouter(Application);
render((
<HashRouter>
<App />
</HashRouter>
), document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-router-dom/5.1.2/react-router-dom.min.js"></script>
<Switch>
...
<Route component={Error} />
<Switch />
If you render a Route but don’t specify a path prop, that route will always be rendered.
If you are using React v6+, instead of the component we have to use element in Route.
And you can specify the error page functional component as shown below:
import React, { Component } from 'react';
import './App.css';
import Login from './component/Login';
import Register from './component/Register';
import Home from './component/Home'
import { BrowserRouter, Route, Routes } from 'react-router-dom';
class App extends Component {
render() {
return (
<div className="App">
<BrowserRouter>
<Routes>
<Route path="/" element={<Login/>}></Route>
<Route path="/register" element={<Register/>}></Route>
<Route path="/home" element={<Home/>}></Route>
<Route path="*" element={<ErrorComponent/>}></Route>
</Routes>
</BrowserRouter>
</div>
);
}
}
function ErrorComponent(){
return <div>Some error occured. Invalid page</div>
}
export default App;

Get path name of route globally in react

I have a basic routing SPA working using react-router-dom v4 and I would like to get the current route (path name) for use in my header (outer context of the app - not sure if that's accurate nomenclature). I want to have a button in my app bar which will do something depending on the route currently in use.
index.js
ReactDOM.render((
<MuiThemeProvider>
<div>
<Router history={history}>
<div>
<Header />
<MainView />
</div>
</Router>
</div>
</MuiThemeProvider>
), document.getElementById('app'));
header.js
class Header extends React.Component {
constructor(props){
super(props);
this.state = {
open: false
};
}
toggleDrawer(){
this.setState({open: !this.state.open}, ()=> {console.log(this.state)});
}
render() {
return (
<div>
<AppBar
iconClassNameRight="muidocs-icon-navigation-expand-more"
onLeftIconButtonTouchTap={()=>{this.toggleDrawer()}}
iconElementRight={<FlatButton label="Create New"/>}
/>
...
In the header.js I want access to the route's pathname to call a certain function from the <FlatButton /> on the right of the appbar. I've tried {this.props.location.pathname} as per the v4 docs but only got errors. TBH I was probably using it wrong though.
That prop is only provided to components rendered as the child of a Route. If you want it somewhere else (like in your Header), you can use the withRouter helper method to inject the props into your component:
// Header.js
import { withRouter } from 'react-router';
// private header class
class Header extends React.Component {
render() {
// you can access this.props.location here
}
}
// wrap Header class in a new class that will render the
// Header class with the current location
// export this class so other classes will render this
// wrapped component
export default withRouter(Header);
// index.js
// ....
ReactDOM.render((
<MuiThemeProvider>
<div>
<Router history={history}>
<div>
<Header />
<MainView />
</div>
</Router>
</div>
</MuiThemeProvider>
), document.getElementById('app'));
You should use react-router-dom if you are not using it (it's the default react-router package for the web now).
import { BrowserRouter, Route, Link } from 'react-router-dom';
ReactDOM.render((
<MuiThemeProvider>
<div>
<BrowserRouter>
<div>
<Route component={Header} />
<Route exact path="/" component={MainView} />
</div>
</BrowserRouter>
</div>
</MuiThemeProvider>
), document.getElementById('app'));
and then from the header.js try using
this.props.location.pathname

React Router component handling as a variable

I am trying to implement React-Router into an existing react app.
How can I use react-router to display components based on some conditions.
var displayRHS;
if(this.state.displayEventComponent){
{/*
* Events Menus
*/}
displayRHS = <DisplayEventComponent
parentFunc={this.displayComponentFunction}
parentPropDay={this.state.day}
/>
} else if (this.state.displayToDoListComponent){
{/*
* To Do List Menu
*/}
displayRHS = <DisplayToDoListComponent
parentCallback_2={this.updateDisplayToDoListComponent}
updateList={this.state.updateDisplayToDoListComponent}
displayIssuesNotList={false}
/>
} else if (this.state.displayIssuesComponent) {
{/*
* Issues menu
*/}
displayRHS = <DisplayIssuesComponent
parentCallback_2={this.updateDisplayToDoListComponent}
updateList={this.state.updateDisplayToDoListComponent}
displayIssuesNotList={true}
/>
}
Displaying Routes breaks
<Route exact path="/" component={displayRHS} />
How can I display these components with their respective props passed in as well?
Many Thanks in advance
PS, I am kind of thinking that a single page should be just that single page and using a routing library should be a sign that you should just have a page refresh instead..
A single page application is called "single page" because the client fetches only one HTML page from the server side. A single page application can have multiple "client-side pages".
The application you are migrating used some condition because it didn't have a router. In react-router, the condition is matching a URL.
The react router allows you to navigate to a client-side page. You will use a component called <Link> to navigate to a client-side page. A virtual page is just a React component. Each available route needs to define a Route. You will need one Route for each client-side page:
<Router>
<Route exact path="/" component={LayoutComponent}>
<IndexRoute component={ToDoPage} />
<Route path="/events" component={EventsPage} />
<Route path="/issues" component={IssuesPage} />
<Route/>
</Router>
The LayoutComponent will always be rendered:
class LayoutComponent extends React.Component {
render() {
return (
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/events">Events</Link></li>
<li><Link to="/issues">Issues</Link></li>
</ul>
{this.props.children}
);
}
}
The value of this.props.children will be the page that matches the URL. So if the URL is /issues the component rendered in {this.props.children} will be the IssuesPage because we configured it that way:
<Route path="/issues" component={IssuesPage} />
Each of your pages can then render the components
ToDoPage:
class ToDoPage extends React.Component {
constructor() {
this.state = {
updateDisplayToDoListComponent: []
};
}
render() {
return (
<DisplayToDoListComponent
parentCallback_2={this.updateDisplayToDoListComponent}
updateList={this.state.updateDisplayToDoListComponent}
displayIssuesNotList={false} />
);
}
public updateDisplayToDoListComponent() {
// do something
}
}
EventsPage:
class EventsPage extends React.Component {
constructor() {
this.state = {
day: 1
};
}
render() {
return (
<DisplayEventComponent
parentFunc={this.displayComponentFunction}
parentPropDay={this.state.day} />
);
}
public displayComponentFunction() {
// do something
}
}
IssuesPage:
class IssuesPage extends React.Component {
constructor() {
this.state = {
updateDisplayToDoListComponent: []
};
}
render() {
return (
<DisplayIssuesComponent
parentCallback_2={this.updateDisplayToDoListComponent}
updateList={this.state.updateDisplayToDoListComponent}
displayIssuesNotList={true} />
);
}
public updateDisplayToDoListComponent() {
// do something
}
}
This is not going to work out of the box and you are going to need to do some reading of the react-router docs but you should have enough details to figure out how to get it to work. You can also lear from "Getting Started with React Router".

how to send value one component to another component in react js?

could you please tell me how to send input field value on second component on button click .I have one button and input field in first component.On button click I need to send input field value to second component
here is my code
http://codepen.io/naveennsit/pen/GZbpeV?editors=0010
var { Router, Route,browserHistory } = ReactRouter
class First extends React.Component{
sendValue(){
browserHistory.push('/second');
}
render(){
return (<div>
<input type='text' />
<button onClick={this.sendValue}>send</button>
</div>)
}
}
class Second extends React.Component{
render(){
return <div>second component</div>
}
}
class Main extends React.Component{
render(){
return (
<Router history={browserHistory}>
<Route path='/' component={First}></Route>
<Route path='/second' component={Second}></Route>
</Router>)
}
}
React.render( <Main />,document.getElementById('app'));
browserHistory.push('/')
The best solution would be to create some architecture that would allow you to have a separate state object, change it, and pass changes on to your components. See Flux, or Redux.
For a pinpointed solution, you could use url params:
class First extends React.Component{
sendValue(){
browserHistory.push('/second/' + this.refs.textField.value);
}
render(){
return (
<div>
<input type='text' ref='textField' />
<button onClick={() => {this.sendValue()}}>send</button>
</div>)
}
}
class Second extends React.Component {
render(){
return <div>second component: {this.props.params.test}</div>
}
}
class Main extends React.Component{
render(){
return (
<Router history={browserHistory}>
<Route path='/' component={First}></Route>
<Route path='/second/:test' component={Second}></Route>
</Router>)
}
}
thanks #omerts, by using refs is a best idea and also another way using state and i am also new to react.
var { Router, Route, browserHistory } = ReactRouter
class First extends React.Component{
constructor(props){
super(props);
this.sendValue = this.sendValue.bind(this);
this.setValue = this.setValue.bind(this);
this.state = {
userID: "#nageshwar_uidev"
};
}
setValue(e){
this.setState({
userID: e.target.value
});
}
sendValue(){
//console.log(this.state.userID);
browserHistory.push('/second/'+this.state.userID);
}
render(){
return (
<div>
<input type='text' value={this.state.userID} onChange={this.setValue} />
<button onClick={this.sendValue}>send</button>
</div>
)
}
}
class Second extends React.Component{
render(){
let { userID } = this.props.params;
return <div>The userID from first component is {userID} <a href="#" onClick={()=>browserHistory.push('/')}>back</a></div>
}
}
class Main extends React.Component{
render(){
return (
<Router history={browserHistory}>
<Route path='/' component={First}></Route>
<Route path='/second/:userID' component={Second}></Route>
</Router>)
}
}
React.render( <Main />,document.getElementById('app'));
browserHistory.push('/')
working example at codepen

React-router: Passing props to children

I'm trying to learn React by building a very basic "portfolio" site with react-router. My main components are: App, Work, Project and ProjectDetail. On the 'Work' page you should be able to see all of the project thumbnails and titles. Clicking on a specific project should route you to that project's detail page, which will include the rest of the project's details. How can I pass the props of Project to ProjectDetail?
My files:
main.js
/*
Routes
*/
var routes = (
<Router history={createHistory()}>
<Route path="/" component={App}>
<IndexRoute component={Work} />
<Route path="work" component={Work} />
<Route path="work/:id" component={ProjectDetail} />
<Route path="about" component={About} />
</Route>
</Router>
);
ReactDOM.render(routes, document.getElementById('main'));
-
App.js
/*
App
*/
class App extends React.Component {
render() {
return (
<div>
<h1>App</h1>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/work">Work</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
{this.props.children}
<footer>footer</footer>
</div>
)
}
}
export default App;
-
Work.js
class Work extends React.Component {
render() {
return (
<div>
<p>Work</p>
<ul>
{/* Need to loop of all projects */}
{PROJECTS.map(project => (
<li key={project.id}>
<Project project={project} />
</li>
))}
</ul>
</div>
)
}
}
export default Work;
-
Project.js
class Project extends React.Component {
render() {
var project = this.props.project;
var linkTo = "/work/" + project.id;
return (
<Link to={linkTo}>
{project.title}
<span> </span>
{project.type}
</Link>
)
}
}
export default Project;
-
ProjectDetail.js
class ProjectDetail extends React.Component {
render() {
return (
<div>
{/* THIS IS WHAT INFORMATION I NEED */}
{this.props.project.title}
{this.props.project.description}
{this.props.project.type}
{this.props.project.technologies}
</div>
)
}
}
export default ProjectDetail;
I can access the data in my Project component, but how can I pass that along to ProjectDetail since I never explicitly use the ProjectDetail component (only used in my routing)?
EDIT: I should add that I suppose I am technically not trying to pass the props to a child since Project is no longer rendered once you are routed to a ProjectDetail. Does that make this impossible?
You need to use the createElement handler for your routes to control the instancing of the route handler. There you can pass props around to the components. React-Router decouples mother-child components, any state propagation needs to be done in the routes.
Update
Based on your edit, in your case ProjectDetail must re-query the applicable project using this.props.params.id, something like...
ProjectDetail.js
class ProjectDetail extends React.Component {
render() {
let project = PROJECTS.find((item)=>item.id == this.props.params.id);
return (
<div>
{project.title}
{project.description}
{project.type}
{project.technologies}
</div>
)
}
}

Categories

Resources