React router 4 nested routes not working - reactjs

I am trying to create a nested route - When user logs in it opens dashboard and when dashboard open i want to create a nested route by making a side menu and change the content at the right but not able to do. When I am trying to access the post page in dashboard it is not opening.
import React from 'react';
import { Switch, Route, Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { userActions } from '../_actions';
import { PostPage } from './PostPage';
import { HomePage } from '../HomePage';
class DashboardPage extends React.Component {
render() {
const { url } = this.props;
return (
<div>
<h1>BasicRouting</h1>
<p>With the help of "Match" Component we can specify the Component we want to render for a particular pattern of the App location/window.pathname.</p>
<p>Select a level from Left Navigation to view the content, also notice the change in URL.</p>
<div className="rightContent">
<p>Second Level Content will appear here:</p>
<Route path={`${this.props.match.url}/post`} component={PostPage} />
</div>
</div>
);
}
}
function mapStateToProps(state) {
console.log(state)
return {
isLoggedIn: state
};
}
const connectedDashboardPage = connect(mapStateToProps)(DashboardPage);
export { connectedDashboardPage as DashboardPage };

There are several problems in your code.
You import { Switch ... } from 'react-router-dom', but never used it afterward.
If you want to call the route in upper/parent components, you need to import { withRouter } to wrap the redux connected class, something like,
const connectedDashboardPage = connect(mapStateToProps)(DashboardPage);
const withRouterConnectedDashboardPage =
withRouter(connectedDashboardPage);
export default withRouterConnectedDashboardPage;
Final suggestion, read through the tutorial here:
https://medium.com/#pshrmn/a-simple-react-router-v4-tutorial-7f23ff27adf
&
always refer to: https://reacttraining.com/react-router/

Related

PubSub not working within route components

I was trying to pass data within route components. Since the react-router-dom remove props for class components in the latest version (v6), I just imported pubsub.js and tried to pass the data through the Link with an onclick event.
Here is the component waiting for publishing (showing part of codes).
import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import PubSub from 'pubsub-js';
export default class ProductDetails extends Component {
state = {
product: {}
}
componentDidMount() {
PubSub.subscribe('product', (_, product) => {
this.setState({product})
})
}
render(){...}
}
Here is the component passing the data:
import React, { Component } from 'react'
import { Link } from 'react-router-dom';
import PubSub from 'pubsub-js';
export default class ProductHome extends Component {
pubProduct = (product) => {
return () => {
PubSub.publish('product', product)
}
}
...
render(){
const product = {a: 1}
<Link
to='/product/details'
onClick={this.pubProduct(product)}
> Details </Link>
...
}
Both of them are route components. I also tried publishSync but still not working. After click the Link, the state in the ProductDetails component did not change.
If router doesn't support pubsub, how to pass data then? I know using hook apis in react-router-dom v6 could be the best way to handle this kind of problem. But for class components, is there a good way to pass any data within route components in v6?
Thanks!

How to use React Context API to have a state across multiple Routes?

I'm trying to understand how the context API works. I'd like to keep a global state that I can update from any Class Component. Currently, when I try to update my Context using the provided function, It only updates the value locally. In my code, I try to update a field "Day" into "Hello", and the change can be seen only when Writer is rendered. As soon as I ask my browser to render "Reader", the value is "Day" again. Why does this happen? Here's my code, I simplified it as much as I could:
index.js:
import React from "react";
import ReactDOM from "react-dom";
import {ThemeContextProvider} from "./ThemeContext";
import App from "./App";
ReactDOM.render(
<ThemeContextProvider>
<App />
</ThemeContextProvider>,
document.getElementById("root")
);
app.js:
import React from "react";
import Writer from "./Writer.js";
import Reader from "./Reader.js";
import { Context } from "./ThemeContext.js";
import {BrowserRouter as Router, Switch, Route} from 'react-router-dom';
class App extends React.Component {
static contextType = Context;
constructor(props) {
super(props);
}
render() {
return (
<div className="app">
<Router>
<Switch>
<Route path="/writer" component={Writer}></Route>
<Route path="/reader" component={Reader}></Route>
</Switch>
</Router>
</div>
);
}
}
export default App;
context.js:
import React, { Component } from "react";
const Context = React.createContext();
const { Provider, Consumer } = Context
// Note: You could also use hooks to provide state and convert this into a functional component.
class ThemeContextProvider extends Component {
state = {
theme: "Day"
};
setTheme = (newTheme) => {
this.setState({theme: newTheme})
};
render() {
return <Provider value={{theme: this.state.theme, setTheme: this.setTheme}}>{this.props.children}</Provider>;
}
}
export { ThemeContextProvider, Consumer as ThemeContextConsumer, Context };
writer.js:
import React from "react";
import {Context} from "./ThemeContext";
class Writer extends React.Component {
static contextType = Context
constructor(props) {
super(props);
this.write = this.write.bind(this)
}
write () {
this.context.setTheme("hello")
}
render() {
return (
<div>
<button onClick={this.write}>press</button>
<p>{this.context.theme}</p>
</div>
);
}
}
export default Writer;
reader.js:
import React from "react";
import { Context, ThemeContextConsumer } from "./ThemeContext";
class Reader extends React.Component {
static contextType = Context;
constructor(props) {
super(props);
}
render () {
return(
<div>
<p>{this.context.theme}</p>
</div>
);}
}
export default Reader;
how do you handle the maneuver to different pages? If right now, you handle it manually by typing it directly in the search top browser input placeholder. Then it will not work since the page getting refresh. Using just context api will not make your data persistant. You need to incorporate the use of some kind of storage to make it persistant.
Anyhow, your code should work if there's not page refresh happen. To see it in different pages tho, you can and a Link (from react-router-dom package) or basically a button to redirect you to different pages, like so:-
just add this in your Writer.js component for testing purposes:-
import React from "react";
import { Link } from 'react-router-dom'
import {Context} from "./ThemeContext";
class Writer extends React.Component {
static contextType = Context
constructor(props) {
super(props);
this.write = this.write.bind(this)
}
write () {
this.context.setTheme("hello")
}
render() {
return (
<div>
<button onClick={this.write}>press</button>
<p>{this.context.theme}</p>
<Link to="/reader">Go to Reader page</Link>
</div>
);
}
}
export default Writer;

How to redirect to some other route after successful authentication in react-admin?

Using react-admin.
I have the same question as this one, because the answer didn't work neither to me nor to the person who asked (see the comments). After the major release update, is there another option to redirect to a custom page after the successful login?
The code:
Inside my custom authProvider, when checking authentication, I am trying to redirect to a custom page:
if (type === AUTH_CHECK) {
...
if (localStorage.getItem("pageID")) {
return Promise.resolve() // goes to Dashboard
else {
return Promise.resolve({ redirectTo: '/pages' }); // needs to go to a custom page
}
However, the Promise.resolve({ redirectTo: '/pages' }) simply does not work.
If I use Promise.reject({ redirectTo: '/pages' }) indeed, it tries to redirect to pages, however, as the AUTH_CHECK is failing, it returns to login and stay in loop.
I also tried put this code inside the AUTH_LOGIN, but it does not work as well.
Currently userLogin action doesn't handle redirect path url that is the reason you are not getting redirected.
There are two ways you can achieve this. Custom login page or Custom dashboard compoenent
Custom login page
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { userLogin } from 'react-admin';
class MyLoginPage extends Component {
submit = (e) => {
e.preventDefault();
// gather your data/credentials here
const credentials = { };
// Dispatch the userLogin action (injected by connect)
if (localStorage.getItem("pageID")) {
this.props.userLogin(credentials, "/pages");
}else {
this.props.userLogin(credentials);
}
}
render() {
return (
<form onSubmit={this.submit}>
...
</form>
);
}
};
export default connect(undefined, { userLogin })(MyLoginPage);
Custom DashBoard
import React, { Component } from 'react';
import { push } from 'react-router-redux';
export class MyDashBoard extends Component {
componentDidMount() {
if (localStorage.getItem("pageID")) {
push("/pages");
}
}
render() {
return (
);
}
};
I couldn't make it work based on other answer (https://stackoverflow.com/a/52360387/986160) so I did like that for react-admin 2.6.2:
https://stackoverflow.com/a/54422728/986160
import React, { Component } from 'react';
import { Redirect } from 'react-router';
import Card from '#material-ui/core/Card';
import CardContent from '#material-ui/core/CardContent';
import CardHeader from '#material-ui/core/CardHeader';
export default class Dashboard extends Component {
render() {
if (localStorage.getItem("user_role") !== "special_role") {
return <Card>
<CardHeader title="Welcome to Dashboard" />
<CardContent></CardContent>
</Card>
}
else {
return (<Redirect to="/route/to/redirect" />);
}
}
}

React + Redux : How to manage transitions when replacing components

I use redux in my react app to dynamically swap components in and out of the DOM. What I'm trying to do is have css transitions happen that fade in and fade out these components.
I thought I could use the CSSTransitionGroup component for that, but as both the entering and leaving components are in the DOM at the same time (new component is mounted while the previous component is still not unmounted), it messes up the layout during these transitions.
I can get either the fade in or fade out to work by not displaying the entering or leaving component, and I played around with css absolute positioning to put one in front of the other during the transition, but both have unwanted side effects.
How does one properly replace one component with another and use transitions on both entering and leaving?
Thanks!
See my component code below just for understanding how I dynamically replace the components using redux.
Component code:
import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import Services from '../components/services';
import About from '../components/about';
import Contact from '../components/contact';
import { connect } from 'react-redux';
import { selectDetail } from '../actions/index';
import { CSSTransitionGroup } from 'react-transition-group'
class Category extends Component {
constructor(props){
super(props);
this.handleDetailSelected = this.handleDetailSelected.bind(this);
}
getSelectedCategory(){
switch (this.props.selectedCategory) {
case 'about':
return <About key={ this.props.selectedCategory } handleDetailSelected={this.handleDetailSelected}/>;
case 'contact':
return <Contact key={ this.props.selectedCategory } handleDetailSelected={this.handleDetailSelected}/>;
default:
return <Services key={ this.props.selectedCategory } handleDetailSelected={this.handleDetailSelected}/>;
}
}
handleDetailSelected(event, detail){
if(detail){
this.props.selectDetail(detail);
}
}
render() {
const category = this.getSelectedCategory();
return (
<CSSTransitionGroup
className="col-xs-12 col-sm-6 content left-content"
component="div"
transitionAppear={true}
transitionAppearTimeout={ 1000 }
transitionName = "category"
transitionEnterTimeout={ 1000 }
transitionLeaveTimeout={ 500 }>
{ category }
</CSSTransitionGroup>
);
}
}
//selectedCategory is set through a navigation component.
function mapStateToProps({ selectedCategory }) {
return { selectedCategory };
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({
selectDetail
}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Category);

Routing in ReactJS with props

I have one main component where I have all state.
And here I passed this states to two different components.
The problem is - I need to open this two components in two different links (<TimeTracker />, <TimeCalendar />).
Render them separately.
How can I made it with React-router? Is it possible?
Bellow is my code for main component
export default class App extends React.Component {
constructor () {
super();
this.initStorage();
this.state = {
startTime: this.getStoreItem('startTime') || 0,
currentTask: this.getStoreItem('currentTask') || '',
results: this.getStoreItem('results') || [],
calendarResults: this.getStoreItem('calendarResults') || []
};
}
/**
create an object in localStorage for timer data if it is not present
*/
initStorage () {
let data = localStorage.getItem('timeData');
if (!data) {
localStorage.setItem('timeData', JSON.stringify({}));
}
}
/**
* get item value from storage
* #param key - item name
*/
getStoreItem = (key) => {
const data = JSON.parse(localStorage.getItem('timeData'));
return data[key];
}
/**
* change item value in storage
* * #param key - item name
* #param value - new value for item
*/
setStoreItem = (key, value) => {
const data = JSON.parse(localStorage.getItem('timeData'));
data[key] = value;
localStorage.setItem('timeData', JSON.stringify(data));
this.setState({
[key]: value
});
}
render () {
const { startTime, currentTask, results, calendarResults } = this.state;
return (
<MuiThemeProvider>
<div>
<TimeTracker
results={results}
setStoreItem={this.setStoreItem}
startTime={startTime}
currentTask={currentTask} />
<TimeCalendar calendarResults={calendarResults} />
</div>
</MuiThemeProvider>
);
}
}
I am new in Routing and did not find some similar examples.
Please help to understand how to do it.
I can make routing for them, but if component do not have props.
But in my example I'm bewildered
Thank you in advance!
Here's an extract from my reactjs code that should help you out :
Router.jsx:
import React from 'react';
import { Router, Route } from 'react-router';
import createBrowserHistory from 'history/createBrowserHistory';
// route components
import { HomePage } from '../Pages/HomePage.jsx';
import { LoginPage } from '../Pages/LoginPage.jsx';
const browserHistory = createBrowserHistory();
export const renderRoutes = () => (
<Router history={browserHistory}>
<div>
<Route exact path="/" component={HomePage}/>
<Route exact path="/login" component={LoginPage}/>
</div>
</Router>
);
In this file, you start off by defining the components that will be rendered following the url adress you will visit on your page. Here is one of these two components:
HomePage.jsx :
import React, { Component } from 'react'
import { Menu, Segment } from 'semantic-ui-react'
import { AppBarEX } from '../components/Appbar.jsx'
export class HomePage extends Component {
render() {
return (
<div>
<AppBarEX />
</div>
)
}
}
HomePage is defined as the landing page in React Router thanks to the "/" path. So when you land on the website you will automatically be directed to the HomePage. In the <AppBarEX/> I use this:
import { Link } from 'react-router-dom'
<Link to = "/login">
The element allows you to define when and how you want to link to other pages, this is probably what you were looking for. In this situation the Link will send you to the login page. To place elements inside your link, you can wrap them within the Link: <Link> your element </Link>
Finally, the element you want to render in your main.jsx goes as follows :
import React from 'react';
import { Meteor } from 'meteor/meteor';
import { render } from 'react-dom';
import { renderRoutes } from './Router.jsx';
Meteor.startup(() => {
render(renderRoutes(), document.getElementById('main_body'));
});
This will allow you to render the renderRoutes defined in the router.jsx. You can find out more here:
https://github.com/reactjs/react-router-tutorial/tree/master/lessons/02-rendering-a-route
Hope this helped you out!
D.

Resources