React-router-dom v.4 BrowseRouter pass function to child - javascript

I have just upgraded to React-Router v.4 (and redux-saga). But I am having problems with passing functions from parent container to child inside a route...
Parent:
import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { BrowserRouter as Router, Route, NavLink } from 'react-router-dom';
import { fetchGalleryImages } from './business_logic/modules/gallery'
import logo from './assets/images/saga-logo.png';
import Gallery from './components/Gallery';
function mapStateToProps(state) {
return { galleryImages: state.galleryImages };
}
function mapDispatchToProps(dispatch) {
return { actions: bindActionCreators({ fetchGalleryImages }, dispatch) };
}
class App extends Component {
constructor(props) {
super(props);
this.loadGallery = props.actions.fetchGalleryImages.bind(this);
}
loadGalleryHandler() {
this.loadGallery();
}
render() {
return (
<div className="App">
<img src={logo} className="logo" alt="logo" />
<h1>Welcome to Redux-Saga</h1>
<section className="content">
<p>This is an exersize in using react together with Redux-saga.</p>
<Router>
<div>
<nav className="main">
<NavLink activeClassName="selected" exact to="/" >Home</NavLink>
<NavLink activeClassName="selected" to="/gallery">Gallery</NavLink>
</nav>
<Route path="/gallery" onLoadEvent={this.loadGalleryHandler} component={Gallery} />
</div>
</Router>
</section>
</div>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
My child component looks like this:
import React, { Component } from 'react';
class Gallery extends Component {
componentDidMount() {
this.props.onLoadEvent();
}
render() {
return (
<div className="Gallery">
<h2>Gallery</h2>
</div>
);
}
}
export default Gallery;
As you can see I am trying to pass the function loadGallery to the Gallery component, however, in the dom the Gallery component gets wrapped in a Route component which does not send the loadGallery function on to its child.
This is what it looks like in React's dom:
<Route path="/gallery" onLoadEvent=loadGalleryHandler() component=Gallery()>
<Gallery match={...somestuff...} location={...somestuff...} history={...somestuff...}>...</Gallery>
</Route>
Clearly the onLoadEvent=loadGalleryHandler() is not passed to Gallery.
How do I make it work?

As you noticed, that props you pass to <Route> won't be passed down to your component. This is the exact use case for a Route's render prop.
Instead of this,
<Route path="/gallery" onLoadEvent={this.loadGalleryHandler} component={Gallery} />
You can do this and then pass any props to your component that you'd like,
<Route path="/gallery" render={() => (
<Gallery {...props} onLoadEvent={this.loadGalleryHandler} />
)} />

Related

Unable to integrate a React ErrorBoundary

I have an ErrorBoundary class (ErrorBoundry.jsx) that looks like this:-
import React, { Component } from 'react'
import ErrorPage from '../../ErrorPage'
const WithErrorBoundary = ({ renderError } = {}) => {
return WrappedComponent => {
return class ErrorBoundary extends Component {
state = { error: null }
componentDidCatch(error, errorInfo) {
this.setState({ error })
}
render() {
if (this.state.error) {
return <ErrorPage />
}
return <WrappedComponent {...this.props} />
}
}
}
}
export default WithErrorBoundary;
The fallback UI (ErrorPage) looks like this:-
import React from 'react';
import { useTranslation } from 'react-i18next';
import classNames from 'classnames';
import Logo from '../../../../../images/logo.svg';
import styles from './styles.module.css';
export default function ErrorPage(props) {
const { t } = useTranslation('common');
return (
<>
<div className={classNames('navbar', 'navbar-fixed-top', styles.headerSection)}>
<div className={classNames('col-md-12', 'col-xs-12' , styles.logoSection)}>
<span className={styles.logo}>
<img className='img-fluid' src={Logo} alt='Logo' />
</span>
</div>
</div>
<div className={styles.content}>
<div className={styles.unavailableDiv}>
<h1>{t('unavailable_page_title')}</h1>
<p>{t('unavailable_page_message_1')}</p>
<p>{t('unavailable_page_message_2')}</p>
</div>
</div>
</>
);
};
I am wrapping my routes in app.jsx with the ErrorBoundary like this:-
const ErrorBoundary = lazy(() => import('./components/core/ErrorBoundary/ErrorBoundry'));
<ErrorBoundary>
<Switch>
<Redirect from='/' to='/notfound' exact />
<Redirect from='/:country/ord' to='/notfound' exact />
<Route path='/notfound' component={NotFound} />
<PrivateRoute path='/:country/' exact component={Payment} />
<PrivateRoute path='/:country/:encryptedParams' exact component={DetailPage} />
<Route component={NotFound} />
</Switch>
</ErrorBoundary>
When I run my app, I get a blank page with a console error:-
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it.
I went through the answers in Functions are not valid as a React child. This may happen if you return a Component instead of from render, but they didn't quite help me. Where exactly am I going wrong?

Getting the value of Props parameter as undefined (ReactJS)

I'm having an attribute in one of the components and when I'm trying to access that attribute via props, I'm getting its value as undefined.
Below is the piece of code where I'm making use of the component and passing the required attribute.
import React, { Component } from "react";
import PageNotFound from "./pages/page-not-found";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import BookSectionPage from "./pages/books-section";
import BookDetails from "./pages/book-details";
class App extends Component {
render() {
return (
<div>
<BrowserRouter>
<Switch>
<Route path="/" exact component={BookSectionPage}/>
<Route path="/book/category/:categoryName" exact render = { (props) => {
return <BookSectionPage title = "JavaScript" /> // This is the component
}} />
<Route path="/book/:bookID" exact component={BookDetails} />
<Route component={PageNotFound} />
</Switch>
</BrowserRouter>
</div>
);
}
}
export default App;
Below is the code for the component where I'm trying to access the above-mentioned attribute via Props but getting its value as undefined.
import React from "react";
import Header from "../components/header/header";
import Footer from "../components/Footer/footer";
import BookSectionComponent from "../components/books-section/books-section";
const BookSectionPage = (Props) => {
let books=[1,2,3,4,5,6];
console.log(Props.title); // Here instead of printing the value of attribute, it's showing undefined.
return (
<div className="has-fixed-footer">
<Header />
<BookSectionComponent title = {Props.title} books = {books} />
<Footer />
</div>
);
};
export default BookSectionPage;
Any help would be highly appreciated. Thanks!
From Parent to Child Using Props
App
└── Parent
├── Child1
Most easiest direction of data flow in React and basic example.
Parent Component
class Parent extends React.Component {
state = { title : "JavaScript" }
render() {
return (
<div>
<Child1 dataFromParent = {this.state.title} />
</div>
);
}
}
Child Component
class Child1 extends React.Component {
render() {
return (
<div>
The data from parent is:{this.props.dataFromParent}
</div>
);
}
}

React Where to place login page for a one page app with a side menu

I have a react web app with a sidemenu. Whenever a user clicks on the link in the sidemenu, they are routed to a page that is rendered at the right side of the sidemenu. My question is, how do I do login for such a usecase seeing as any page I route to renders to the right of the sidemenu. I want the login page to be full screen without the side menu showing. This is what App.js looks like.
import React, { Component } from "react";
import { HashRouter } from "react-router-dom";
import Navigation from "./pages/General/components/Navigation";
import SideMenu from "./pages/General/components/SideMenu";
import "../src/css/App.css";
class App extends Component {
render() {
return (
<div>
<HashRouter>
<div className="main-wrapper">
<SideMenu />
<Navigation />
</div>
</HashRouter>
</div>
);
}
}
export default App;
Here is Navigation.js
import React from "react";
import { Route } from "react-router-dom";
import CalendarPage from "../../Calendar/CalendarPage";
import DoctorsList from "../../Doctors/DoctorsList";
import PatientsList from "../../Patients/PatientsList";
import AdminUsersList from "../../AdminUsers/AdminUsersList";
import SpecialitiesList from "../../Specialities/SpecialitiesList";
const Navigation = () => {
return (
<div className="mainarea">
<Route exact path="/" component={CalendarPage} />
<Route exact path="/scheduler" component={CalendarPage} />
<Route exact path="/doctors" component={DoctorsList} />
<Route exact path="/patients" component={PatientsList} />
<Route exact path="/admin-users" component={AdminUsersList} />
<Route exact path="/specialities" component={SpecialitiesList} />
</div>
);
};
export default Navigation;
The best solution I can figure out in terms of a clean design, is to implement another router in your App.jsx, because you are implementing the routing inside your component, and you need another one for your login page.
Then, your App.jsx could be like this:
import React, { Component } from "react";
import { Redirect, Route, Switch } from "react-router-dom";
import LogIn from "./pages/General/components/Login";
import HomePage from "./pages/General/components/HomePage";
import "../src/css/App.css";
class App extends Component {
render() {
return (
<div>
<Switch>
<Route path={'/login'} component={LogIn} />
<Route path={'/'} component={HomePage} />
<Redirect to="/" />
</Switch>
</div>
);
}
}
export default App;
Then, for your HomePage do the following
import React, { Component } from "react";
import { HashRouter } from "react-router-dom";
import Navigation from "./pages/General/components/Navigation";
import SideMenu from "./pages/General/components/SideMenu";
import "../src/css/App.css";
class HomePage extends Component {
render() {
return (
<div>
<HashRouter>
<div className="main-wrapper">
<SideMenu />
<Navigation />
</div>
</HashRouter>
</div>
);
}
}
export default HomePage;
I hope it helps!
Here is my solution, it not exactly a solution, but it will give you a basic idea on how to implement this.
The idea is to place the Login component in app.js, and conditionally display it if the user is logged in.
You will have to pass a handler function to login component through which you will be able to control app.js state.
When login will be sucessfull, u can show the Navigation and Sidemenu component.
import { Fragment } from "react";
import Login from "path/to/login";
class App extends Component {
state = { isLoggedIn: false };
loginHandler = () => {
this.setState({
isLoggedIn: true
});
};
render() {
return (
<div>
<div className="main-wrapper">
{isLoggedIn ? (
<Fragment>
<SideMenu />
<Navigation />
</Fragment>
) : (
<Login loginHandler={this.loginHandler} />
)}
</div>
</div>
);
}
}
Also you need write a separate router file, which will contain the main app.
This is used to show the app component when navigated to /
import React from 'react';
import { HashRouter, Route } from 'react-router-dom';
import App from './app';
const MainRoute = () => (
<HashRouter>
<Route path="/" component={App} />
</HashRouter>
);
export default MainRoute;

Using react api context alongside react router

I have a problem with passing context to route. I get an error when i click a link that goes to my component where context was passed from App component. Below is that component with App (only one import just to show where Context is coming from):
App.js
import { Context } from './Context';
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
cryptolist: []
}
}
componentDidMount = () => {
fetch('https://api.coinmarketcap.com/v2/ticker/?structure=array')
.then(response => response.json())
.then(json => this.setState({
cryptolist: json.data
}))
}
render() {
return (
<div>
<Menu />
<Context.Provider value={this.state}>
<Userlist />
</Context.Provider>
</div>
)
}
}
export default App;
Userlist.js ( should be cryptolist or something )
import { Context } from '.././Context'
export default class Userlist extends Component {
render() {
return (
<main>
<Context.Consumer>
{(context) => context.cryptolist.map(el => {
return (
<div>
<h2>{el.name}</h2>
<h5>{el.symbol}</h5>
<h3>{el.quotes.USD.price}</h3>
</div>
)
})}
</Context.Consumer>
</main>
)
}
}
Context.js
import React from 'react';
export const Context = React.createContext();
Everything works just fine here untill i wanted to make a menu that links to this component.
import React from "react";
import { slide as Slider } from 'react-burger-menu';
import { BrowserRouter as Router, Route, Link, Switch} from "react-router-dom";
import Main from './main';
import Userlist from './userlist';
export default class Menu extends React.Component {
render () {
return (
<Router>
<div className="bg-navy w-100 h-100">
<Slider width={ 180 } isOpen={ false }>
<Link className="menu-item" to="/main">Home</Link>
<Link className="menu-item" to="/crypto">About</Link>
</Slider>
<Switch>
<Route path="/main" component={Main} />
<Route path="/crypto" component={Userlist} />
</Switch>
</div>
</Router>
);
}
}
When i click a link to component Userlist i get an error thats cryptolist is not defined. I get it that Userlist can't see a context after clicking link to it. How to pass it correctly?
You are using the routes in the Menu component. Is this really you want? Though, I don't know how this slide thingy works. Maybe this is the way you want to go. I think your problem occurs because your Menu component is not wrapped by the provider. Try like this:
<Context.Provider value={this.state}>
<Menu />
<Userlist />
</Context.Provider
Your Menu component will call Userlist but as it is out the Provider the context doesn’t exist!
Replace Userlist in Context.Provider by Menu and all will be fine.

React not displaying correct route

I am putting together a small application to get used to React, now I have installed React-Router-Dom and when I click a link the URL correctly changes. The issue is that the correct Component does not display.
index.js
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById('root')
);
registerServiceWorker();
App.js
import React, { Component } from 'react';
import Sidebar from './Components/Sidebar';
import SidebarItem from './Components/SidebarItem';
import Home from './Components/Home';
import './App.scss';
import { Link, Route, Switch } from 'react-router-dom';
class App extends Component {
constructor() {
super();
this.state = {};
}
render() {
return (
<div className="App">
<Link to='/home'>Home</Link>
<Switch>
<Route path='/home' Component={Home} />
</Switch>
</div>
);
}
}
export default App;
Can anyone tell me the reason why HomeComponent does not appear?
The prop of the Route that takes a component is spelled component with a small c, not Component.
Example
function Home() {
return <div> Home </div>;
}
class App extends Component {
render() {
return (
<Router>
<div>
<Link to="/home">Home</Link>
<Switch>
<Route path="/home" component={Home} />
</Switch>
</div>
</Router>
);
}
}

Categories

Resources