I was building a search engine for custom project.
There I have a search bar from where user can search.
When the user searches, I want the given link to work as it works in case of google
www.google.com/ search? queryRelatedInfo
Notice the search? and then whatever query/parameter/ID
for this I tried something like this in
import React, {Component} from 'react';
import {
BrowserRouter,
Route,
Switch,
Redirect,
} from 'react-router-dom';
import SearchScreen from "./container/searchScreen.js"
import HomeScreen from "./container/home.js";
class route extends Component {
render () {
return (
<BrowserRouter>
<div>
<Switch>
<Route path ="/" exact render ={(props) => <HomeScreen {...props}/>} />
<Route path ="/search?:id" exact render ={(props) => <SearchScreen {...props}/>} />
</Switch>
</div>
</BrowserRouter>
)
}
}
export default route
Notice, <Route path ="/search?:id" above.
Unfortunately this didn't worked out.
I understand that <Route path ="/:id" works but how can i make <Route path ="/search?:id to work i.e how can I make some link like http://localhost:3000/search?9e9e to work
I think this is related with historyApiFallback. That parameter;
(https://webpack.js.org/configuration/dev-server/#devserver-historyapifallback)
When using the HTML5 History API, the index.html page will likely have to be served in place of any 404 responses. devServer.historyApiFallback is disabled by default. Enable it by passing:
module.exports = {
//...
devServer: {
historyApiFallback: true
}
};
Your react app is a single page application. So all path except home path actually is an virtual path, they are not physically exist. The paths must routed to home path. So react-router can manage.
you don't need to put the path like this /search?:id, just put it search
<Route path ="/search" exact render ={(props) => <SearchScreen {...props}/>} />
then inside your SearchScreen component, get the value of search parameter from the URL, check this issue will help.
after the user make search, pass the value like this /search?s=value_here
Related
I'm working in a project that uses:
react/react-dom#16.9.0
#loadable/component
styled-components
react-router-dom
The application renders both server side and client side.
I'm using #loadable/component to dynamically code split this way.
router.tsx
import * as React from 'react'
import loadable from '#loadable/component'
import { Route, Switch } from 'react-router-dom'
const NotFound = loadable(() =>
import('../components/NotFound/NotFound' /* webpackChunkName: "notfound" */)
)
const routes = (
<Switch>
<Route component={NotFound} />
</Switch>
)
export default routes
When loading the page, this error appear on the console and the page seems to flick for a second.
react-dom.development.js:546 Warning: Did not expect server HTML to contain a <div> in <main>.
When I check the output in both sides (server/client), they are identical.
When I remove #loadable/component like bellow, it works and the error is gone.
router-without-loadable.tsx
import * as React from 'react'
import { Route, Switch } from 'react-router-dom'
import NotFound from '../components/NotFound/NotFound'
const routes = (
<Switch>
<Route component={NotFound} />
</Switch>
)
export default routes
Seems to be something to do with #loadable/component but I'm not 100% sure.
Finally have an answer for this:
For #loadable/component to work properly, you need to put the magic webpack comment (/* webpackChunkName: "notfound" */) before the path of the file this way.
const NotFound = loadable(() =>
import(/* webpackChunkName: "notfound" */ '../components/NotFound/NotFound')
)
Reference:
https://github.com/smooth-code/loadable-components/issues/23
And more important, in the server side, you need to wrap you app in a ChunkExtractorManager and pass the client extractor (I was passing the server extractor, it's not very clear in the docs).
const statsFile = path.resolve('./wwwroot/dist/loadable-stats.json')
const extractor = new ChunkExtractor({
statsFile,
entrypoints: ['client'] // name of the proper webpack endpoint (default: main)
})
<ChunkExtractorManager extractor={extractor}>
<App />
</ChunkExtractorManager>
Here is a proper clear example on how to implement it:
https://github.com/iamssen/seed
Update 24.09.2019
Added to the official docs
https://www.smooth-code.com/open-source/loadable-components/docs/server-side-rendering/#chunkextractor-entrypoints
I think the problem is your NotFound component is not loaded and thus Route dont know what to render which is causing the error.
You would need to modify something like below:
<Route path="/404/" exact component={props => <NotFound {...props} />} />
I am using react router with my application and everything works fine locally. Below is my code. The issue is when I host it online it is in a my-application subdirectory:
https://www.example.com/my-application
This is breaking the code and it is not functioning as it should.
can anyone advise how to fix this issue? If there is anything unclear please feel free to ask. I have tried countless approaches but cannot get it to work. One answer can be seen here.
the issue I am getting is that with the current code it navigates to https://www.example.com/test as opposed to https://www.example.com/my-application/test. I thought if I hardcoded in the url it would work but this also breaks the application.
The code below is slightly whittled down to remove superfluous code.
import React, { Component } from 'react';
import styled from 'styled-components'
import { Route, Switch } from 'react-router-dom'
const Home = ({ avatarUrl }) => {
return (
<div>
<Route exact path="/" component={Overview}/>
<Route path="/test" component={Test}/>
</div>
);
};
class App extends Component {
render() {
return (
<section>
<Nav avatarUrl={ this.props.avatarUrl }/>
<Switch>
<Route path="/example" component={Example}/>
<Route path="/" render={()=><Home
avatarUrl={ avatarUrl }
/>}/>
</Switch>
</section>
)
}
}
export default App
Apply config changes pacakage.json add homepage page as your folder name
To solve this I just did two things:
Added "hostname": "https://www.example.com/my-application" at the top of package.json.
Added basename="my-application" to the BrowserRouter e.g. <BrowserRouter basename="my-application">
Note: I created the project using create-react-app. I am using react version 18 and react-router version 6.
I'm using HashRouter to setup my App like so:
import {HashRouter as Router, Route, Switch, Link} from 'react-router-dom';
const Routing = () => (
<Router>
<div>
<Route exact path ="/" component = {App} />
<Route path ="/about" component = {About} />
</div>
</Router>
)
It works great, but when I click a link to go to /about and then hit the back button nothing happens. How do I make it so my internet browser's back button will take me back to the previous page? The app is built using create-react-app if that makes a difference.
Use Switch
Switch is unique in that it renders a route exclusively. In
contrast, every Route that matches the location renders inclusively.
This means that the way you are doing it all routes are being rendered because they match.
If the URL is /about, then About, User, and NoMatch will all
render because they all match the path. This is by design, allowing us
to compose Routes into our apps in many ways, like sidebars and
breadcrumbs, bootstrap tabs, etc.
Now, if we're at /about, Switch will start looking for a matching
Route. Route path="/about" will match and Switch will stop
looking for matches and render About
Your code should look like this
import {HashRouter as Router, Route, Switch, Link} from 'react-router-dom';
const Routing = () => (
<Router>
<Switch>
<Route exact path ="/" component = {App} />
<Route path ="/about" component = {About} />
</Switch>
</Router>
)
I have mixed content on my homepage. The user specific content is an edit button next to their own content.
When the user logs out via a logout route this code gets executed:
import React from 'react'; // needed
import ReactDOM from 'react-dom';
import Home from './components/layout/Home.js';
import Login from './Login/Login';
import PollDetails from './components/layout/PollDetails.js';
import EditPoll from './components/presentation/EditPoll.js';
import CreatePoll from './components/presentation/CreatePoll';
import Container from './components/containers/Container.js';
import {Route,Router,browserHistory,IndexRoute} from 'react-router';
import Auth from './utils/Auth';
const mountNode = document.getElementById('root');
ReactDOM.render(
<Router history={browserHistory}>
<Route path="/" component={Container} >
<IndexRoute component={Home} />
<Route path="login" component={Login} />
<Route path="logout" onEnter={(nextState, replace) => {
Auth.deauthenticateUser();
console.log('Logging out src/app.js');
Auth.clearCookie();
// change the current URL to /
replace('/');}} />
<Route path="Polldetailfull/:id" component={PollDetails} />
<Route path="Editthepoll/:id" component={EditPoll} />
<Route path="createPoll" getComponent={(location, callback) => {
if (Auth.isUserAuthenticated()) {
callback(null, CreatePoll);
} else {
callback(null, Home);
}
}} />
</Route>
</Router>,mountNode);
However, the replace('/'); sends you back to the home page but doesn’t re-render any components. Note there is no state to change here. Do I need a state to force the re-render?
Note, if you press refresh on the browser the desired behaviour happens. I tried looking on React Router's code but could not find much about events. To be honest, I don't fully understand onEnter={(nextState, replace) =>
You could use location.reload() to cause it to re-render
onEnter -> this function use when component going to render on browser.
For your idea, You need to use route the component based log details in component life cycle of particular components.below code to be use in path component life cycle(componentWillMount or componentDidMount) and route the page as want .
browserHistory.push('/location-path-name');
call the component based log details in router like below sample
<Route path="createPoll" component={Auth.isUserAuthenticated() ? CreatePoll : Home}/>
I'm trying to migrate to use React Router 4 and having some trouble understanding the logic of the <Switch> component as it's used in the docs to handle a 404 (or unmatched) route.
For my entry JavaScript file, I have the following routes set up.
index.js
<Switch>
<Route path="/login" component={Login} />
<Route path="/forgot-password" component={ForgotPassword} />
<Route path="/email-verification" component={EmailVerification} />
<Route component={App} />
</Switch>
The Login component will check to see if the user is authenticated, and if so, redirect the user to the /dashboard route (via history.replace).
The App component is only accessible when the user is authenticated and it has a similar check to redirect the user to /login if she is not.
In my App component I have more specified routes that I can be sure are only accessible if the user is logged in.
App.js
<Switch>
<Route path="/dashboard" component={Dashboard} />
<Route path="/accounts" component={Account} />
<Authorize permissions={['view-admin']}>
<Route path="/admin" component={Admin} />
</Authorize>
<Route path="/users" component={Users} />
<Route component={NotFound} />
</Switch>
Herein lies my problem. The Authorize component checks against the permissions passed to see if the user has those permissions, if so, it renders the children directly, if not, it returns null from render().
The expected behavior here is that the <Route path="/admin" /> does not render at all when there are insufficient permissions and the <Route component={NotFound} /> component renders.
According to the docs:
A renders the first child that matches. A
with no path always matches.
However, if I go to any route declared after the <Authorize> component, the router is matching to null. This means that, based on the example above, going to /users returns null. Is the expected behavior of react-router to return the first match in a <Switch/> component, even if it's a null value?
How can I provide a "catch-all" route (404) for such a situation without creating a <PrivateRoute> component for each of the many, authenticated routes in App.js? Should a null value really produce a match?
Unfortunately, react-router's Switch component won't work with routes nested inside other components like in your example. If you check the docs for Switch, it says:
All children of a <Switch> should be <Route> or <Redirect> elements.
... so your Authorize component is not actually legal there as a direct child of Switch.
If you have a read through the source code of the Switch component, you'll see that it rather evilly reads the props of each of its children and manually applies react-router's matchPath method on each child's path (or from) prop to determine which one should be rendered.
So, what's happening in your case is Switch iterates through its children until it gets to your Authorize component. It then looks at that component's props, finding neither a path or from prop, and calls matchPath on an undefined path. As you note yourself, "a <Route> with no path always matches", so matchPath returns true, and Switch renders your Authorize component (ignoring any subsequent Routes or Redirects, since it believes it found a match). The nested '/admin' route inside your Authorize component doesn't match the current path however, so you get a null result back from the render.
I'm facing a similar situation at work. My plan to work around it is to replace react-router's Switch in my routing code with a custom component which iterates through its children, manually rendering each one in turn, and returning the result of the first one that returns something other than null. I'll update this answer when I've given it a shot.
Edit: Well, that didn't work. I couldn't work out a supported way to manually invoke "render" on the children. Sorry I couldn't give you a workaround to Switch's limitations.
In case anyone reads this in >= 2019, one way to deal with this behaviour is to simply wrap the Route-component like so:
import React from 'react'
import { Route } from 'react-router-dom'
type Props = {
permissions: string[]
componentWhenNotAuthorized?: React.ElementType
}
const AuthorizedRoute: React.FunctionComponent<Props> = ({
permissions,
componentWhenNotAuthorized: ComponentWhenNotAuthorized,
...rest
}) => {
const isAuthorized = someFancyAuthorizationLogic(permissions)
return isAuthorized
? <Route {...rest} />
: ComponentWhenNotAuthorized
? <ComponentWhenNotAuthorized {...rest} />
: null
}
export default AuthorizedRoute
Then, simply use it as such:
import React from 'react'
import { Route, Switch } from 'react-router-dom'
import AuthorizedRoute from 'some/path/AuthorizedRoute'
import Account from 'some/path/Account'
import Admin from 'some/path/Admin'
import Dashboard from 'some/path/Dashboard'
import NotFound from 'some/path/NotFound'
import Users from 'some/path/Users'
const AppRouter: React.FunctionComponent = () => (
<Switch>
<Route
component={Account}
path='/accounts'
/>
<AuthorizedRoute
component={Admin}
componentWhenNotAuthorized={NotFound}
path='/admin'
permissions={['view-admin']}
/>
<Route
component={Dashboard}
path='/dashboard'
/>
<Route
component={Users}
path='/users'
/>
<Route
component={NotFound}
/>
</Switch>
)
export default AppRouter
Similar idea to what Robert said, here's how I did it
class NullComponent extends React.Component {
shouldComponentBeRenderedByRoute() {
return false;
}
render() {
return null;
}
}
class CustomSwitch extends React.Component {
render() {
return (
// React.Children.map returns components even for null, which
const children = React.Children.toArray(this.props.children).map(child => {
const { render, shouldComponentBeRenderedByRoute } = child.type.prototype;
if (shouldComponentBeRenderedByRoute && !shouldComponentBeRenderedByRoute.call(child)) {
return null;
}
if (shouldComponentBeRenderedByRoute) {
return render.call(child);
}
return child;
});
return <Switch>{children}</Switch>;
);
}
}
then use it just do
<CustomSwitch>
<Route path... />
<NullComponent />
<Route path... />
</CustomSwitch>
here, a component without shouldComponentBeRenderedByRoute function is assumed to be a valid Route component from react-router, but you can add more condition (maybe use path props) to check if it's a valid Route