React router dom v5 Nested routes never matching - react-router

I have two main pages: LoginPage and HomePage. HomePage is main page with navbar and content. I'm trying to implement nested routing.
loginPage
HomePage
SchoolsPage
UserDetails
I prepared routing for them:
<Switch>
<ProtectedRoute
key="loginPage"
path={`${ROOT_PATHS.login}`}
component={AuthPageContainer}
shouldBeRedirected={authState === 'AUTHENTICATED'}
authenticationPath={`${ROOT_PATHS.version}`}
/>
<ProtectedRoute
key="homePage"
path={ROOT_PATHS.version}
component={HomePageContainer}
shouldBeRedirected={authState === 'NOT_AUTHENTICATED'}
authenticationPath={`${ROOT_PATHS.login}`}
/>
</Switch>
Home page:
export const HomePage: React.FC<HomePageProps> = ({ fetchUserInfo, user, isLoading, userLogout }): JSX.Element => {
useEffect(() => {
fetchUserInfo()
}, [fetchUserInfo])
return (
<LoadingContent isLoading={isLoading}>
<Navbars user={user} userLogout={userLogout} />
<Pages />
</LoadingContent>
)
}
An routings in Pages:
export const Pages: React.FC = (): JSX.Element => {
const { url, path } = useRouteMatch()
return (
<Switch>
<Route path={`${url}${ROOT_PATHS.userDetails}`} exact component={UserDetailsContainer} />
<Route path={`${url}${ROOT_PATHS.schools}`} exact component={SchoolListContainer} />
<Route path={`${path}`} exact>
{console.log('redirected')}
<Redirect to={`${url}${ROOT_PATHS.schools}`} />
</Route>
</Switch>
)
}
And paths:
export const ROOT_PATHS = {
login: '/login',
version: '/v1',
schools: '/schools',
userDetails: '/userDetails'
}
My index.tsx:
// axios should be configured before saga because saga makes requests on startup
configureAxios()
const store = configureStore()
export const DOMStructure: React.FC = (): JSX.Element => (
<Suspense fallback={<Spinner animation="border" role="status" />}>
<Provider store={store}>
<ConnectedRouter history={history}>
<CookiesProvider>
<AppContainer />
</CookiesProvider>
</ConnectedRouter>
</Provider>
</Suspense>
)
ReactDOM.render(<DOMStructure />, document.getElementById('root'))
It all works fine when navigating via links in navbar.
But when I refresh page, it never matches to any child-routes and is redirected to v1/schools (I checked that with console.log('redirected').
So I cannot render page under specific ur. When I put 'localhost:3000/v1/userDetails' in browser, it's always redirected to 'localhost:3000/v1/schools'.
Why is that? What i'm missing?

Related

Props object is empty with history.push

I have an onClick handler used for filtering data that routes back to the same component, but with a different url. However, when this same components renders again, I cannot access props.location.
Lots of code has been left out for brevity.
Component:
import { useHistory } from 'react-router-dom';
const Dashboard = (props) => {
const history = useHistory();
console.log(props) // Empty
useEffect(() => {
console.log(props) // Empty
})
const handleFilter = argument => {
history.push('/filter'); // 'argument' left out to test routing, and to ensure props.location is accessible
}
return (
<button onClick={() => handleFilter('someArgumentHere')}>Filter</button>
)
}
Router:
import React from 'react';
import {
BrowserRouter as Router,
Switch,
Route,
Redirect,
} from 'react-router-dom';
const PrivateRoute = ({ exact, path, component: Component }) => {
return (
<Route
exact={exact}
path={path}
render={props => (
<div>
<Navbar />
<Component {...props} />
</div>
)}
/>
);
};
<Router>
<Switch>
<PrivateRoute exact component={Dashboard} path="/" />
<PrivateRoute exact component={Dashboard} path="/filteredPriority" />
</Switch>
</Router>
When I click on handleFilter, the routing works. Meaning, my browser navigates from '/' to '/filteredPriority', and I see the same content, which is desired. But, since this is a filter, I want to access the url params via props.location, and it's empty. I don't know why.
Figured it out. Needed to wrap my Dashboard component in withRouter.

React router params not passing

I am trying to go to page and pass id, but my ownParams in mapStatetoProps remains empty. Do note the url generated is correct.
My current structure is
index.jsx
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
app.jsx
class App extends Component {
render() {
return (
<div>
<Nav />
<Router />
</div>
);
}
}
export default App;
router.jsx
const Router = () => (
<router>
<Switch>
<Route path='/signup' component={Signup} />
<Route path='/browse' component={Browse}/>
<Route path='/detail/:id' component={Detail} />
</Switch>
</router>
)
export default Router;
detail.jsx
export default class App extends Component {
render() {
return (
<div>
<Info />
<Message/>
</div>
);
}
}
Now in info.jsx at the end of the file
function mapStateToProps({posts}, ownProps){
console.log('>>>>>>>> post_show State to props', ownProps);
return { post : posts[ownProps.match.params.id]};
}
export default connect(mapStateToProps, { fetchPost, deletePost })(PostsShow);
this gives me an error because ownProps is empty.

React router throws and error when getting async component

I'm using react, redux and react router amoung others to build and example app.
I'm trying to load asynchronously different parts of my application. I've divided my app in ducks and I'm following this example https://github.com/insin/react-examples/tree/master/code-splitting-redux-reducers
But I'm getting this error:
Uncaught Invariant Violation: The root route must render a single element
When trying to get async component with getComponent method of react router.
I'm using:
react-router 2.0.1
My routes:
export default function configureRoutes(reducerRegistry) {
return(
<Route>
<Route component={Landing}>
<Route path='/login' component={Login}/>
<Route path='/register' component={Register}/>
</Route>
<Route path="admin" getComponent={(location, cb) => {
require.ensure([], require => {
cb(null, require('./containers/admin'))
})
}}/>
<Route component={App}>
<Route path='/' component={Home} />
</Route>
</Route>
)}
My component
class Admin extends Component {
componentDidMount() {
this.props.load()
}
render() {
const { message, isFetching } = this.props
return (
<div>
<p>{message}</p>
<p>This module was loaded via chunk </p>
{loading && <p>Doing some fake loading ...</p>}
</div>
)
}
}
Admin.propTypes = {
message: PropTypes.string.isRequired,
isFetching: PropTypes.bool.isRequired,
load: PropTypes.string.isRequired
}
const mapStateToProps = state => state.admin
function mapDispatchToProps(dispatch) {
return bindActionCreators({ load }, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(Admin)
Does anyone have the same error? any ideas? Anyone have something similar working?
Thanks community!
Update: Added index.js for clarity
import configureRoutes from './routes'
import configureStore from './store/configureStore'
import coreReducers from './modules/core'
import ReducerRegistry from './reducer-registry'
var reducerRegistry = new ReducerRegistry(coreReducers)
// Configure hot module replacement for core reducers
if (process.env.NODE_ENV !== 'production') {
if (module.hot) {
module.hot.accept('./modules/core', () => {
var nextCoreReducers = require('./modules/core')
reducerRegistry.register(nextCoreReducers)
})
}
}
const routes = configureRoutes(reducerRegistry)
const store = configureStore(reducerRegistry)
render(
<I18nextProvider i18n={i18n}>
<Provider store={store}>
<Router history={browserHistory} routes={routes} />
</Provider>
</I18nextProvider>,
document.getElementById('root')
)
I think your root <Route> is missing the component field.
You need to specify either component or getComponent for every parent route, as this will be the component that the current child route’s component gets passed to as this.props.children.
Rather than
export default function configureRoutes(reducerRegistry) {
return (
<Route>
you want something like
export default function configureRoutes(reducerRegistry) {
return (
<Route component={App}>
In this case, you probably won’t need another App route below.

Routing is not work with react-router-redux actions

When use the Link, router works as expected, though i get a warning [history] pushState is deprecated; use push instead.
Using routeActions from react-router-redux does't work, url was change (http://localhost:3002/addCity), but view still the same (Home) or show error if i go to page by url for example: localhost:3002/addCity.
git: https://github.com/kirsanv43/Weather.git
reducers:
export default combineReducers({
cities,
routing: routeReducer
});
store config: https://github.com/kirsanv43/Weather/blob/master/app/redux/config/store.js
import rootReducer from '../reducers'
export default function configureStore(initialState) {
const history = createHistory();
const middleware = syncHistory(history)
const finalCreateStore = compose(
applyMiddleware(middleware)
)(createStore)
const store = finalCreateStore(rootReducer)
middleware.listenForReplays(store);
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('../reducers', () => {
const nextRootReducer = require('../reducers').default
store.replaceReducer(nextRootReducer)
})
}
return store
}
Router:
const store = configureStore()
ReactDOM.render(
<Provider store={store}>
<Router history={browserHistory}>
<Route path="/" component={App}>
<IndexRoute component={Home}/>
<Route path="addCity" component={AddCity}/>
</Route>
</Router>
</Provider>
,
document.getElementById('root')
);
Component:
class CitiesList extends React.Component {
onAddCity = () => {
this.props.route.push('/addCity')
};
render() {
return <div className="weatherContainer">
<ul>
<li className="listItem addBtn"><a onClick={this.onAddCity}><span>ADD CITY</span></a></li>
</ul>
</div>
}
}
function mapDispatchToProps(dispatch) {
return {
route:bindActionCreators(routeActions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(CitiesList)
insted createHistory need use createHashHistory from 'history'
that working for me
Please try to change "react-router-redux" version to "^5.0.0-alpha.8". It solved my problem

React Router, this.props.children == null when refreshing page

So I've got React-Router set up as follows:
// App.js
render(){
console.log(this.props)
return (
<div>
<Header />
{this.props.children}
<Footer />
</div>
)
}
// routes.js
export default (
<Router>
<Route path="/" component={App} >
<IndexRoute component={HomePage} />
<Route path="/destination" component={DestinationIndexHandler} />
<Route path="/destination/:slug" component={DestinationHandler} />
<Route path="/destination/:slug/:article" component={ArticleHandler} />
<Route path="/article" component={ArticleIndexHandler} />
<Route path="/article/:slug" component={ArticleHandler} />
<Route name='contact' path="/contact" component={ContactHandler} />
<Route path="*" component={NotFoundHandler}/>
</Route>
</Router>
);
And finally:
// server.js
app.get(/^(?!.*(api|.js|.css)).*$/, function (req, res) {
let location = createLocation(req.url);
console.log("LOCAL")
match({ routes, location }, (error, redirectLocation, renderProps) => {
console.log(location);
if (error) {
res.status(500).send(error.message)
} else if (redirectLocation) {
res.redirect(302, redirectLocation.pathname + redirectLocation.search)
} else if (renderProps) {
let content = ReactDOMServer.renderToString(<RoutingContext {...renderProps} />);
console.log(content);
res.status(200).render('index', {
content: content,
staticPort: DEV_PORT
});
} else {
res.status(404).send('Not found')
}
})
});
For some reason, when I refresh the page, the server side renders the view successfully but the browser renders the home view (IndexRoute). If you remove the IndexRoute, it only renders the header and footer.
If I log in the terminal I get 3 returns, the first and second being:
CHILDREN { '$$typeof': Symbol(react.element),
type: [Function: ContactHandler],
and finally a call to NotFoundHandler, even though the route is there. Any ideas?