React.lazy: this.props is empty - reactjs

I have simple test project with App.js
import React, { Component, Suspense } from 'react';
import { BrowserRouter, Route, NavLink } from 'react-router-dom';
const Posts = React.lazy(() => import('./containers/Posts'));
class App extends Component {
render() {
return (
<BrowserRouter>
<React.Fragment>
<nav>
<NavLink to="/posts/222">Posts Page</NavLink>
</nav>
<Route path="/posts/:id" render={() => (
<Suspense fallback={<div>Loading...</div>}>
<Posts {...this.props} />
</Suspense>
)}
/>
</React.Fragment>
</BrowserRouter>
);
}
}
export default App;
and containers/Posts.js
import React, {Component} from 'react';
class Posts extends Component {
componentDidMount() {
console.log(this.props);
}
render() {
return (<h1>The Posts Page</h1>)
}
};
export default Posts;
and this.props in Posts is an empty object.
How can I get this.props.match.params.id in this case?

Wrap Posts with withRouter from react-router-dom
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
class Posts extends Component {
componentDidMount() {
console.log(this.props);
}
render() {
return (<h1>The Posts Page</h1>)
}
};
export default withRouter(Posts);

Related

react, axios and route

I'm testing some function, I discover a solution but I'm not sure that is correct way.
I need to load data from API so I used axios, and I need to share data between some child routes, I am currently using simple setState to keep values and passed them to the Route Components to be used.
Here is the MainContainer component
import React from 'react';
import axios from 'axios';
import { BrowserRouter, Route, Switch, Link, HashRouter, useLocation } from 'react-router-dom';
import Languages from './pages/Languages';
import Home from './pages/Home';
export default class MainContainer extends React.Component
{
constructor(props) {
super(props);
this.state = {
languages: null,
language:null,
}
}
get_init(language)
{
console.log("get_init")
var params = {
language:language,
}
axios
.get("https://xxxx.com/wp/it/api/init/")
.then(function(result) {
this.setState({languages:result.data.languages});
}.bind(this));
}
render() {
console.log("render")
return (
<div>
<Switch>
<Route path="/" component={() => <Languages get_init={this.get_init.bind(this)} languages={this.state.languages} />} exact />
<Route path="/:language/" component={(urlparam) => <Home language={urlparam.match.params.language} get_init={this.get_init.bind(this)} languages={this.state.languages} />} exact/>
<Route component={Error}/>
</Switch>
</div>
);
}
}
My Language Component is bellow
import axios from 'axios';
import React from 'react';
import LoaderData from './../helpers/LoaderData';
import { Link } from 'react-router-dom';
export default class Languages extends React.Component
{
constructor(props)
{
super(props);
if (props.languages==null)
{
this.props.get_init();
}
}
render() {
if (this.props.languages==null)
{
return '';
}
return (
<div>
<div className='main'>
LANGUAGE {Math.random()}<br/>
{
this.props.languages.map((language, i) =>
{
return (<div key={language.prefix}><Link to={language.prefix+"/"} >{language.name}</Link> <br/></div>)
})
}
</div>
</div>
);
}
}
is a correct way to build a website in react or there are better ways?

React context : state flow broken from Provider to Consumer

I want to try to transfer the record from state through several components using Consumer and Provider. I have a value in the ShoppingBasketProvider component that I want to pass to the ShoppingBasket component, but at this point it doesn't output anything and equals undefined. Tell me what I'm doing wrong.
I have next components:
ShoppingBasketContext:
import React, { Component, PureComponent } from "react";
export const ShoppingBasketContext = React.createContext();
export default class ShoppingBasketProvider extends Component {
state = {
shoppingBasketItems: 11
}
render() {
return (
<ShoppingBasketContext.Provider shoppingBasketItems={ this.state.shoppingBasketItems }>
{this.props.children}
</ShoppingBasketContext.Provider>
)
}
}
app:
import React, {PureComponent} from 'react'
import './App.scss';
import Shop from '../../page/shop/shop.js'
import AboutUs from '../../page/aboutUs/aboutUs.js'
import Menu from '../menu/menu.js'
import ShoppingBasketProvider from '../shoppingBasketProvider/shoppingBasketProvider.js'
import { BrowserRouter, Switch, Route, Redirect, Router } from "react-router-dom";
export default class App extends PureComponent {
render() {
return (
<main>
<ShoppingBasketProvider>
<BrowserRouter>
<Menu />
<Switch>
<Route path="/" exact children={<Shop />} />
<Route path="/aboutus" children={<AboutUs />} />
<Redirect to="/" />
</Switch>
</BrowserRouter>
</ShoppingBasketProvider>
</main>
);
}
}
ShoppingBasket:
import React, {PureComponent} from 'react'
import { ShoppingBasketContext } from '../shoppingBasketProvider/shoppingBasketProvider.js';
export default class ShoppingBasket extends PureComponent {
render() {
return (
<div>
<ShoppingBasketContext.Consumer>
{shoppingBasketItems => (
<React.Fragment>
Shopping cart: <span>{shoppingBasketItems}</span>
</React.Fragment>
)}
</ShoppingBasketContext.Consumer>
</div>
)
}
}
The prop you pass to the provider must be called value. See suggested change below
return (
<ShoppingBasketContext.Provider value={ this.state.shoppingBasketItems }>
{this.props.children}
</ShoppingBasketContext.Provider>
)

React Router props.history

I'm a new to React.js with asp.net core, and I got an error that I don't understand.
I created protected route like that :
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import auth from './auth.js'
export const ProtectedRoute = ({ component: Component, ...rest }) => {
return (
<Route
{...rest}
render = {props => {
if(auth.estAuthentifier()){
return <Component {...props}/>
} else {
return <Redirect to={
{
pathname: "/",
state: {
from: props.location
}
}
} />
}
}}
/>
);
}
In my App.js, I added the protected route like that :
import React, { Component } from 'react';
import { BrowserRouter, Switch, Route } from 'react-router';
import { Login } from './components/Login'
import { Layout } from './components/Layout';
import { Home } from './components/Home';
import { ProtectedRoute } from './components/protected.route';
export default class App extends Component {
static displayName = App.name;
render () {
return (
<div>
<Switch>
<Route exact path="/" component={Login} />
<Layout>
<ProtectedRoute exact path="/home" component={Home}/>
</Layout>
<Route path="*" component={() => "404 NOT FOUND"}/>
</Switch>
</div>
);
}
}
And, the Layout component render all components with my NavBar included :
import React, { Component } from 'react';
import { Container } from 'reactstrap';
import { NavMenu } from './NavMenu';
import '../css/Layout.css';
export class Layout extends Component {
static displayName = Layout.name;
render () {
return (
<div className="conteneur-principal">
<NavMenu />
<Container>
{this.props.children}
</Container>
</div>
);
}
}
The problem is the next : I put the logout button in my navigation bar component but when I'm trying to logout and redirect to Login, i got the following error : TypeError: Cannot read property 'push' of undefined
NavMenu.js :
import React, { Component } from 'react';
import { NavLink } from 'reactstrap';
import { Link } from 'react-router-dom';
import auth from './auth';
import { withRouter } from 'react-router-dom';
import '../css/SideBarNav.css';
export class NavMenu extends Component {
static displayName = NavMenu.name;
constructor(props) {
super(props);
console.log(this.props);
this.deconnexion = this.deconnexion.bind(this);
}
deconnexion = () => {
auth.logout(() => {
this.props.history.push('/'); //LINE IN ERROR
})
}
render () {
return (
<div className="conteneur-menu">
<img src="images/familieu-logo-clear.png" alt="logo-familieu" title="logo-familieu" className="is-clickable logo-familieu"/>
<div className="conteneur-link">
<NavLink tag={Link} className="menu-link" to="/">Home</NavLink>
</div>
<input type="button" value="Déconnexion" className="btn-deconnexion" onClick={this.deconnexion}/>
</div>
);
}
}
What should I do to make my logout button work ?
Thanks alot
In NavMenu.js you are importing the withRouter, but is not used.
Wrap the export of component with withRouter like this:
export default withRouter(NavMenu);

React-router renders component when I update the browser

I am working with react-router and react-md. My problem is that when I change url through links the components not renders. If I press F5 works. My code is this:
Verions
"react-router": "^5.0.0",
"react-router-dom": "^5.0.0"
App.js
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
sidebarVisible: false
};
}
render() {
const { sidebarVisible } = this.state;
return (
<div>
<Header showDrawer={this.showDrawer} />
<Sidebar handleVisibility={this.handleVisibility} closeDrawer={this.closeDrawer} sidebarVisible={sidebarVisible} />
<Content />
</div>
);
}
}
export default App;
index.js
import React from 'react';
import { render } from 'react-dom';
import { BrowserRouter as Router } from 'react-router-dom';
WebFontLoader.load({
google: {
families: ['Roboto:300,400,500,700', 'Material Icons'],
},
});
render(
<Router>
<App />
</Router>
, document.getElementById('root'));
serviceWorker.unregister();
content.js
import React, { Component } from 'react'
import PropTypes from 'prop-types';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
class Content extends Component {
render() {
const { title } = this.props;
return (
<Router>
<div>
<Route exact path="/client" component={Client} />
<Route path="/payment" component={Payment} />
<Route path="/invoice" component={Invoice} />
</div>
</Router>
);
}
}
export default Content
NavItemLink.js
import React from 'react';
import { Link, Route } from 'react-router-dom';
const NavItemLink = ({ label, to, icon, exact }) => (
<Route path={to} exact={exact}>
{({ match }) => {
let leftIcon;
if (icon) {
leftIcon = <FontIcon>{icon}</FontIcon>;
}
return (
<ListItem
component={Link}
active={!!match}
to={to}
primaryText={label}
leftIcon={leftIcon}
/>
);
}}
</Route>
);
export default NavItemLink;
I think that in the content.js, react-router must detect the change and to renders the components.

componentWillEnter not being called with TransitionGroup

I'm trying to add page transitions to my app with react router 4. For some reason the functions componentWillEnter, componentWillLeave are not being called. Does someone has any idea why?
index.js
import React from 'react';
import { render } from 'react-dom';
import { BrowserRouter as Router } from 'react-router-dom';
import App from './containers/App';
render(
<Router>
<App />
</Router>,
document.getElementById('root')
);
components/App.js:
import React, { Component } from 'react';
import { Route, Link } from 'react-router-dom';
import TransitionGroup from 'react-transition-group/TransitionGroup';
import Home from '../components/home/Home';
import Users from '../components/users/Users';
const firstChild = (props) => {
const childrenArray = React.Children.toArray(props.children);
return childrenArray[0] || null;
};
class App extends Component {
render() {
return (
<div className="App">
<ul className="TopBar">
<Link to="/">Home</Link>
<Link to="/users">Users</Link>
</ul>
<Route
exact
path="/"
children={({ match, ...rest }) => (
<TransitionGroup component={firstChild}>
{match && <Home {...rest} />}
</TransitionGroup>
)} />
<Route
path="/users"
children={({ match, ...rest }) => (
<TransitionGroup component={firstChild}>
{match && <Users {...rest} />}
</TransitionGroup>
)} />
</div>
);
}
}
export default App;
components/AnimatedWrapper.js
import React, { Component } from 'react';
const AnimatedWrapper = WrappedComponent => class AnimatedWrapper extends Component {
constructor(props) {
super(props);
this.state = {
animate: 0
};
}
componentWillAppear(cb) {
console.log('WillAppear');
this.setState({
animate: 1
});
cb();
}
componentWillEnter(cb) {
console.log('WillEnter');
this.setState({
animate: 1
});
cb();
}
componentWillLeave(cb) {
console.log('WillLeave');
this.setState({
animate: 0
});
setTimeout(() => cb(), 175);
}
render() {
console.log(this.state.animate);
const style = {
opacity: `${this.state.animate}`,
};
return (
<div style={style} className="animated-page-wrapper">
<WrappedComponent {...this.props} />
</div>
);
}
};
export default AnimatedWrapper;
components/Home.js
import React, { Component } from 'react';
import AnimatedWrapper from '../animated/AnimatedWrapper';
class HomeComponent extends Component {
render() {
return (
<div className="page">
<h1>Home</h1>
<p>Hello from the home page!</p>
</div>
);
}
}
const Home = AnimatedWrapper(HomeComponent);
export default Home;
components/Users.js:
import React, { Component } from 'react';
import AnimatedWrapper from '../animated/AnimatedWrapper';
class UsersComponent extends Component {
render() {
return (
<div className="page">
<h1>Users</h1>
<p>Hello from users page!</p>
</div>
);
}
}
const Users = AnimatedWrapper(UsersComponent);
export default Users;
EDIT: I think TransitionGroup cannot work this way. It's low level component and it cannot reach the componentWillEnter() hook inside the <Switch> and <Route>.
You can use ReactCSSTransitionGroup instead.
Or try something more complicaded like this: https://hackernoon.com/animated-page-transitions-with-react-router-4-reacttransitiongroup-and-animated-1ca17bd97a1a

Resources