I am trying to reload onto the same route without having to refresh the page. For this specific case, using history.pushState(), but I'm getting an error:
TypeError: history.pushState is not a function.
Here is my code:
import React from 'react';
import PropTypes from 'prop-types';
import { Container } from 'kawax-js';
import { Switch, Route } from 'react-router-dom';
import File from './FileContainer';
import Folder from './FolderContainer';
import HomeContainer from './HomeContainer';
class RootContainer extends React.Component {
static stateToProps = ({ ownProps, select }) => {
const files = select('files');
const lastFile = _.last(files);
return ({
lastFile: lastFile || {}
})
};
static propTypes = {
history: PropTypes.object.isRequired
};
static defaultProps = {
lastFile: {}
};
render() {
const { lastFile, history } = this.props;
if( lastFile === {} || !lastFile.isUploaded
|| lastFile.isUploaded === null) {
return (
<Switch>
<Route exact path="/" component={HomeContainer} />
<Route exact path="/file/:itemPath/:refHash" component={File} />
<Route exact path="/:folderName" component ={Folder}/>
</Switch>
);
}
return history.pushState(null, "/:folderName")
}
}
export default Container(RootContainer);
Is there a better way of doing this or am I missing something here?
You may get the desired result by forcing the component to rerender, take a look at the documentation here. I see you are extending React.Component so you should be able to do the following:
...
constructor() {
this.reload = this.reload.bind(this);
}
...
reload() {
this.forceUpdate();
}
...
I know it does not use history but there will be no other code required as it is included with the Component class.
please use this code
Router.browserHistory.push('/');
instaed of history.pushState(null, "/:folderName")
You have few possibilities to do that, currently my favorite way to do that is using anonymous function in component prop:
<Switch>
<Route exact path="/" component={()=><HomeContainer/>} />
<Route exact path="/file/:itemPath/:refHash" component={()=><File/>} />
<Route exact path="/:folderName" component ={()=><Folder/>}/>
</Switch>
Or if you want to refresh with current url params, you'll need extra route (reload), and play a little with router stack:
reload = ()=>{
const current = props.location.pathname;
this.props.history.replace(`/reload`);
setTimeout(() => {
this.props.history.replace(current);
});
}
<Switch>
<Route path="/reload" component={null} key="reload" />
<Route exact path="/" component={HomeContainer} />
<Route exact path="/file/:itemPath/:refHash" component={File} />
<Route exact path="/:folderName" component ={Folder}/>
</Switch>
<div onCLick={this.reload}>Reload</div>
Related
I would like to refresh the current page (home) when the user tries to go back via browser, after logged in.
What's the best way to solve this? Any suggestions?
I was trying to do something like this inside index.tsx:
if (id) {
const rollback = history.goBack();
if (rollback) {
history.push('/');
}
}
Obs: In this case, '/' is my home page, and i can't apply the logic above because "An expression of type 'void' cannot be tested for truthiness".
Sorry for anything i'm still new at react and trying to learn.
Don't know if i could do something inside my router, here it is anyway:
import React, { Suspense, lazy } from 'react';
import { Route, Router, Switch } from 'react-router-dom';
import history from '../utils/history';
import LoadingPage from '../components/organisms/LoadingPage';
const DownloadExams = lazy(() => import('../pages/private/DownloadExams'));
const Home = lazy(() => import('../pages/private/Home'));
const ProfileSelector = lazy(() => import('../pages/private/ProfileSelector'));
const AppRoutes = () => {
return (
<Router history={history}>
<Suspense fallback={<LoadingPage />}>
<Switch>
<Route exact path={'/'} component={Home} />
<Route exact path={'/baixar-exames'} component={DownloadExams} />
<Route exact path={'/profile'} component={ProfileSelector} />
</Switch>
</Suspense>
</Router>
);
};
export default AppRoutes;
Any suggestions?
Thanks!
User logIn time you can store a token or flag and store it in localStorage. After that, you can check if the user login so redirects to the page. You can also create some HOC for the same.
Example :
import React from "react";
import { Route, Redirect } from "react-router-dom";
export const ProtectedRoute = ({ component: Component, ...rest }) => {
const isLoggedIn = localStorage.getItem("token");
return (
<Route
{...rest}
render={(props) => {
if (isLoggedIn) {
return <Component {...props} />;
} else {
return (
<Redirect
to={{
pathname: "/",
state: {
from: props.location,
},
}}
/>
);
}
}}
/>
);
};
Example Usage :
<ProtectedRoute path="/home" exact component={Home} />
This will redirect the user to /home after login.
I have a simple app that's using redux and react-router. I wrapped my app component in a provider tag so that it has access to the store. I connected (in App.js) the mapStateToProps and mapStateToDispatch in the App.js. I'm not sure how to pass the function I defined in App.js to a child component since I'm using route. I tried doing the render trick but it didn't work. If I can pass it to that CelebrityPage component, how would I receive it in the file? Any help would be appreciated.
This is my App.js
import React, { Component } from 'react';
import { connect } from 'react-redux'
import './App.css';
import Clarifai from 'clarifai'
// import Particles from 'react-particles-js';
// import particlesOptions from './particleOptions'
import { Signin } from './components/signin/Signin';
import Register from './components/register/Register';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'
import { setSearchField } from './context/Actions'
import FacePage from './Containers/FacePage';
import CelebrityPage from './Containers/CelebrityPage';
import ControllerPage from './Containers/ControllerPage';
const mapStateToProps = state => {
return {
input: state.input
}
}
const mapDispatchToProps = (dispatch) => {
return {
handleSearchChange: (event) => dispatch(setSearchField(event.target.value))
}
}
...
render() {
return (<Router>
<Switch >
<Route path='/celebrity' exact render={props => <CelebrityPage{...props} handleSearchChange={this.handleSearchChange} />} />
<Route path='/' exact component={Register} />
<Route path='/signin' exact component={Signin} />
<Route path='/contoller' exact component={ControllerPage} />
<Route path='/face-detection' exact component={FacePage} />
</Switch>
</Router>)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App)
If you are going to pass store actions and states into the child components, it means you are refusing to use the advantages of redux. The best approach should be connect any of your component that needs to access to the actions or state to the store. Doing connection at the root component level and passing the props to the child components is not a good solution.
I think what robert is saying is what you'd probably want to do. Don't try to pass your props inside of your <Route>. Instead do your connect mapDispatchToProps and your mapStateToProps inside your CelebrityPage Component.
Once you do the wrapping inside of the Celebrity Page component you should have access to the props and functions that you have defined.
...
// keep all the previous imports from your App.Js
render() {
// have your router like this
return (<Router>
<Switch >
<Route path='/celebrity' exact component ={CelebrityPage} />
<Route path='/' exact component={Register} />
<Route path='/signin' exact component={Signin} />
<Route path='/contoller' exact component={ControllerPage} />
<Route path='/face-detection' exact component={FacePage} />
</Switch>
</Router>)
}
}
export default App
Example Celebrity page
import React from 'react'
import { connect } from 'react-redux'
class CelebrityPage extends React.Component {
// put your mapStateToProps and mapDispatch function heres instead of app.js
mapStateToProps() {
}
mapDispatchToProps {
// bind your handlesearch function to props here
}
render() {
return (
<div>
<input />
<button onClick={this.props.handleSearchChange}/>
</div>
)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(CelebrityPage)
I am trying to learn how to redirect through pages using React.
I have tried to write some code on my own but i keep getting problems. I Created a route class for the class path, and 2 classes to move through. And the route class is imported to the app class. I am not pasting any data from the second class because its a written paragraph to be displayed.
This is what i have done:
import React from 'react'
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom';
import Firsttry from './firsttry'
import Comp2 from "./comp2";
const Routes = () => (
<Router>
<Switch>
<Route path="/" component={Firsttry} />
<Route path="/comp2" component={Comp2} />
</Switch>
</Router>
);
export default Routes;
Second class:
import React, { Component } from "react";
import { Redirect } from "react-router-dom";
class Firsttry extends Component {
constructor(props) {
super(props);
this.state = {
redirect: false
};
}
onclick = () => {
this.setState({
redirect: true
});
};
render() {
if (this.state.redirect) {
return <Redirect to="/comp2" />;
}
return (
<div>
<p> hello</p>
<button onClick={this.onclick}>click me</button>
</div>
);
}
}
export default Firsttry;
Switch the routes. May be always your first route is getting hit and Comp2 is never rendered.
<Switch>
<Route path='/comp2' component={Comp2} />
<Route path='/' component={Firsttry}/>
</Switch>
Or you have another option: adding exact prop to your route.
<Switch>
<Route exact path='/' component={Firsttry}/>
<Route exact path='/comp2' component={Comp2} />
</Switch>
Only one Route inside a Switch can be active at a time, and / will match every route. You can add the exact prop to the / route to make sure it will only match on the root path.
const Routes = () => (
<Router>
<Switch>
<Route exact path="/" component={Firsttry} />
<Route path="/comp2" component={Comp2} />
</Switch>
</Router>
);
I am building a styleguide app. I have two dropdown components where a user can choose both a brand and a component - the app will then display the chosen component branded according to the selected brand. I want both of these options to be included in the URL.
The two dropdown's that are programatically changing the route. I am getting the error TypeError: Cannot read property 'history' of undefined whenever the user interacts with either dropdown.
I have a component being rendered by a route :
<Route path="/:brand/:component"
render={props => <DisplayComponent {...props} />} />
That component has two event handlers for two dropdown component that let the user select the root:
handleComponentPick(event: any) {
const component = event.target.value;
this.props.history.push(`/${this.props.match.params.brand}/${component}`);
}
handleBrandChange = (event: any) => {
if (event.target instanceof HTMLElement) {
const brand = event.target.value;
this.props.history.push(`/${brand}/${this.props.match.params.component}`);
}
};
render = () => {
return (
<div className={"constrain-width-wide center "}>
<ThemePicker
component={this.props.match.params.component}
brand={this.props.match.params.brand}
handleBrandChange={this.handleBrandChange}
handleComponentPick={this.handleComponentPick}
/>
<div className="currently-selected-component" />
<Route path="/:brand/button" component={Button} />
<Route path="/:brand/card" component={Card} />
</div>
);
};
}
I am wrapping the whole app in the Router.
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById("root")
);```
If you are getting this error inside a test using jest, you need to wrap your componen within a router. I am using react-testing-library, so my logic looks as follows:
import { render, cleanup } from '#testing-library/react'
import { BrowserRouter as Router } from 'react-router-dom'
import YourComponent from '../path/to/YourComponent'
// ...
describe('YourComponent component', () => {
afterEach(cleanup)
it('matches snapshot', () => {
const { asFragment } = render(
// The following will solve this issue
<Router>
<YourComponent />
</Router>
)
expect(asFragment()).toMatchSnapshot()
})
})
Can you try these following changes
handleComponentPick(event: any) { to handleComponentPick = (event: any) => {
then
render = () => { to render() {
Hope this works.
you have to pass the history like
<Router history={browserHistory} routes={routes} />,
that way, you can use history with props to navigate.
font: https://github.com/ReactTraining/react-router/blob/v3/docs/guides/Histories.md
try to use browserHistory on you app.js, like
render(
<Router history={browserHistory}>
<Route path='/' component={App}>
<IndexRoute component={Home} />
<Route path='about' component={About} />
<Route path='features' component={Features} />
</Route>
</Router>,
document.getElementById('app')
)
that way, you are passing history for all of your another router.
We need to pass history as a prop to Router. I am expecting that you are using react router v4 aka react-router-dom.
import { createBrowserHistory } from "history";
import { Router } from "react-router-dom";
const history = createBrowserHistory();
...
<Router history={history}>
<Routes />
</Router>
Demo : https://codesandbox.io/s/yv5y905ojv
Spied on the useHistory() hook and provided the mock route data.
import routeData from 'react-router';
describe('<Component /> container tests', () => {
beforeEach(() => {
const mockHistory = {
pathname: '/dashboard'
};
jest.spyOn(routeData, 'useHistory').mockReturnValue(mockHistory);
});
Please I have an issue building a multi-tenant SaaS solution. For every tenant, I want them to use a subdomain, so i can get the subdomain from the url, make a call to a REST api that returns data about that tenant.
For example,
the admin (another app entirely - admin app) creates a tenant with domain name: tenant1.
In the tenant application on my local system, I was able to go to tenant1.localhost:3000. I get the url, and get the domain name. I then make a call with the domain to get the theme of tenant (this is stored in localStorage).
Unfortunately, we deploy on k8 in my company and so I couldn't mimic this behavior. So i have been advised by the devOps team to use subdomain in the context, thereby having localhost:3000/tenant1. Remember the tenant is dynamic, so i tried this:
<BrowserRouter basename={"/:tenant"}>
<Switch>
<Route exact path="/login" name="Login" component={Login} />
<Route exact path="/set-password/:token" name="Set Password" component={SetPassword} />
<PrivateRoute path="/" name="Default Layout" component={DefaultLayout} />
</Switch>
</BrowserRouter>
The solution above however makes my url to localhost:3000/:tenant/login
Please how can i use dynamic basename in the router, so it can accept:
localhost:3000/tenant1
localhost:3000/tenant3
localhost:3000/tenant2 etc.
It can allow any, my app handles wrong domain inputted
I finally used dynamic tenant with the following code
class App extends Component {
state = {
domain: ""
}
componentWillMount () {
const { domain } = this.state;
const parsedData = window.location.pathname.split("/");
let domain = parsedData[1];
this.setState({ domain: domain })
this.props.onGetTenant(domain);
}
render () {
const { domain } = this.state;
return () {
<BrowserRouter basename={"/"+domain}>
<Switch>
<Route exact path="/login" name="Login" component={Login} />
<Route exact path="/set-password/:token" name="Set Password" component={SetPassword} />
<PrivateRoute domain={domain} path="/" name="Default Layout" component={DefaultLayout} />
</Switch>
</BrowserRouter>
}
const mapStateToProps = state => {
const { tenant} = state;
return { tenant};
};
const mapDispatchToProps = (dispatch) => {
return {
onGetTenant: bindActionCreators( tenantActions.get, dispatch)
}
};
export default connect(mapStateToProps, mapDispatchToProps)(App)
This worked for me using react >16 and react-router-dom v5
export const App = () => {
return (
<BrowserRouter>
<Switch>
<Route path="/:tenantId?" component={LayoutRoot} />
</Switch>
</BrowserRouter>
);
};
export const LayoutRoot = () => {
var { tenantId } = useParams();
//TODO: add some validation here and inform user if tenant is invalid
return (
<BrowserRouter basename={tenantId}>
<Switch>
<Route path="/login" component={LoginComponent} />
<Route path="/dashboard" component={DashboardComponent} />
</Switch>
</BrowserRouter>
);
};
You can render updates to your router's basename by using the key property. Any changes to the key value will cause the component to re-render.
Here's a code sandbox to demo:
https://codesandbox.io/s/react-router-dom-dynamic-basename-forked-hnkk0?file=/index.js
You can hover or inspect the links in the sandbox to verify that their href values are correctly updating after changing the basename. You can also see that the hrefs won't update if you remove the key property from Router.
import React, { useState } from "react";
import { render } from "react-dom";
import { BrowserRouter, Link } from "react-router-dom";
const Root = () => {
const [count, setCount] = useState(1);
const basename = `basename-${count}`;
return (
<BrowserRouter basename={basename} key={basename}>
<Link to="/link1">Link 1</Link>
<br />
<Link to="/link2">Link 2</Link>
<br />
Current basename: {basename}
<br />
<button onClick={() => setCount(count + 1)}>change basename</button>
</BrowserRouter>
);
};
render(<Root />, document.getElementById("root"));
Here's a codesandbox and the utility I wrote:
https://codesandbox.io/s/react-router-dom-dynamic-basename-xq9tj?file=/index.js
import urlJoin from 'url-join';
// it's important to have an identifier in their app
export const APP_ROOT_URL = '/my-app';
export const getBaseUrlPath = () => {
const currentPath = document.location.pathname || APP_ROOT_URL;
const startOfAppBase = currentPath.indexOf(APP_ROOT_URL);
let base = currentPath;
if (startOfAppBase !== -1) {
base = currentPath.substr(0, startOfAppBase);
}
base = urlJoin(base, APP_ROOT_URL);
return base;
};