I am try to make inventory list app with react and redux , but I have a small problem with need some understanding about it .
I know we have few way to create react components and try to get as much as possible all information , but still not sure why this issue is happen .
I have app.js which is my all React-Router set there
console.log('The application has been start...');
import React from 'react'
import { render } from 'react-dom'
import { IndexPage } from './modules/IndexPage'
import { AddItemForm } from './modules/ui/AddItemForm'
import { PageNotFound } from './modules/PageNotFound'
import { Router, Route, hashHistory } from 'react-router'
import routes from './routes'
window.React = React
render(
<Router history={hashHistory}>
<Route path='/' component={IndexPage}/>
<Route path='/add' component={AddItemForm}/>
<Route path='*' component={PageNotFound}/>
</Router>,
document.getElementById('react-container')
)
for IndexPage and PageNotFound the router display and render correctly , But for add , is display blank page with no error .
import { PropTypes } from 'react'
const AddItemForm = ({ onNewItem=f=>f, router}) => {
//const AddItemForm = () => {
let _itemName
const submit = e => {
e.preventDefault()
onNewItem({
itemName: _itemName.value,
itemCount: 1
})
router.push('/')
_itemName.value = ''
}
return (
<form onSubmit={submit} >something
<label htmlFor="item-name"> Name Item</label>
<input id="item-name" type="text" ref={(input) => _itemName = input } />
<button>Add Item</button>
</form>
)
}
AddItemForm.propTypes = {
onNewItem: PropTypes.func,
router: PropTypes.object
}
export default AddItemForm
in order to make sure there is something wrong with React-Router or the components which I made I change the AddItemForm with below code
export const AddItemForm = () =>
<div>
<h1>Oops ! - The page is working!</h1>
</div>
which start working normally which shows something wrong with my components but I am not able to understand what is the main issue. Please give me hit or point where is the issue or what is the different ?
Here is link of github for full source of application so far
https://github.com/msc/Inventory-List
I tried to reproduce this issue. What I figured out is that you also need to import React for your component along with PropTypes.
So try importing React
import React, { PropTypes } from 'react'
in your AddItemForm component. Then worked for me.
Related
I am trying to use React Shepherd to create a walkthrough for my application.
I can't seem to find anything that explains how to switch routes within the tour. window.location.replace = "/someurl" refreshes the page and kills the tour completely. I am trying to achieve something along the lines of this
History.js
import { createBrowserHistory } from "history";
const history = createBrowserHistory();
export default history;
steps.js
import hist from "./History";
const Steps = [
{
//...
when: {
hide: () => {
hist.push("/someurl");
},
},
},
//...
]
export default Steps;
App.js
import React from "react";
import { Router } from "react-router";
import { Route } from "react-router-dom";
//...
import Steps from "./Steps";
import hist from "./History";
import "shepherd.js/dist/css/shepherd.css";
const tourOptions = {
defaultStepOptions: {
cancelIcon: {
enabled: true,
},
classes: "shepherd-theme-custom",
},
useModalOverlay: true,
};
const App = () => {
return (
<Router history={hist}>
<Route exact path="/signin" component={SignIn} />
<ShepherdTour steps={Steps} tourOptions={tourOptions}>
<PrivateRoute exact path="/*" component={Main} />
</ShepherdTour>
</Router>
);
};
export default App;
When the steps' hide function is called, the url path is switched but the page is not rendered. I am wondering if I am using react-router wrong or is there a different way to go about this?
So this actually had nothing to do with React Shepherd at all, this was purely a React Router issue. I mistakenly nested two BrowserRouter's since it was also accidentally included in my Main component. Once removed the application navigates with custom history file just fine.
I'm rendering components from my external (node_modules) pattern library. In my main App, I'm passing my Link instance from react-router-dom into my external libraries' component like so:
import { Link } from 'react-router-dom';
import { Heading } from 'my-external-library';
const articleWithLinkProps = {
url: `/article/${article.slug}`,
routerLink: Link,
};
<Heading withLinkProps={articleWithLinkProps} />
In my library, it's rendering the Link as so:
const RouterLink = withLinkProps.routerLink;
<RouterLink
to={withLinkProps.url}
>
{props.children}
</RouterLink>
The RouterLink seems to render correctly, and even navigates to the URL when clicked.
My issue is that the RouterLink seems to have detached from my App's react-router-dom instance. When I click Heading, it "hard" navigates, posting-back the page rather than routing there seamlessly as Link normally would.
I'm not sure what to try at this point to allow it to navigate seamlessly. Any help or advice would be appreciated, thank you in advance.
Edit: Showing how my Router is set up.
import React from 'react';
import { hydrate, unmountComponentAtNode } from 'react-dom';
import { AppContainer } from 'react-hot-loader';
import { Provider } from 'react-redux';
import { createBrowserHistory } from 'history';
import { ConnectedRouter } from 'react-router-redux';
import RedBox from 'redbox-react';
import { Route } from 'react-router-dom';
import { Frontload } from 'react-frontload';
import App from './containers/App';
import configureStore from './redux/store';
import withTracker from './withTracker';
// Get initial state from server-side rendering
const initialState = window.__INITIAL_STATE__;
const history = createBrowserHistory();
const store = configureStore(history, initialState);
const mountNode = document.getElementById('react-view');
const noServerRender = window.__noServerRender__;
if (process.env.NODE_ENV !== 'production') {
console.log(`[react-frontload] server rendering configured ${noServerRender ? 'off' : 'on'}`);
}
const renderApp = () =>
hydrate(
<AppContainer errorReporter={({ error }) => <RedBox error={error} />}>
<Provider store={store}>
<Frontload noServerRender={window.__noServerRender__}>
<ConnectedRouter onUpdate={() => window.scrollTo(0, 0)} history={history}>
<Route
component={withTracker(() => (
<App noServerRender={noServerRender} />
))}
/>
</ConnectedRouter>
</Frontload>
</Provider>
</AppContainer>,
mountNode,
);
// Enable hot reload by react-hot-loader
if (module.hot) {
const reRenderApp = () => {
try {
renderApp();
} catch (error) {
hydrate(<RedBox error={error} />, mountNode);
}
};
module.hot.accept('./containers/App', () => {
setImmediate(() => {
// Preventing the hot reloading error from react-router
unmountComponentAtNode(mountNode);
reRenderApp();
});
});
}
renderApp();
I've reconstructed your use case in codesandbox.io and the "transition" works fine. So maybe checking out my implementation might help you. However, I replaced the library import by a file import, so I don't know if that's the decisive factor of why it doesn't work without a whole page reload.
By the way, what do you mean exactly by "seamlessly"? Are there elements that stay on every page and should not be reloaded again when clicking on the link? This is like I implemented it in the sandbox where a static picture stays at the top on every page.
Check out the sandbox.
This is the example.js file
// This sandbox is realted to this post https://stackoverflow.com/q/59630138/965548
import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import { Heading } from "./my-external-library.js";
export default function App() {
return (
<div>
<img
alt="flower from shutterstock"
src="https://image.shutterstock.com/image-photo/pink-flowers-blossom-on-blue-600w-1439541782.jpg"
/>
<Router>
<Route exact={true} path="/" render={Welcome} />
<Route path="/article/coolArticle" component={CoolArticleComponent} />
</Router>
</div>
);
}
const Welcome = () => {
const articleWithLinkProps = {
url: `/article/coolArticle`,
routerLink: Link
};
return (
<div>
<h1>This is a super fancy homepage ;)</h1>
<Heading withLinkProps={articleWithLinkProps} />
</div>
);
};
const CoolArticleComponent = () => (
<div>
<p>This is a handcrafted article component.</p>
<Link to="/">Back</Link>
</div>
);
And this is the my-external-library.js file:
import React from "react";
export const Heading = ({ withLinkProps }) => {
const RouterLink = withLinkProps.routerLink;
return <RouterLink to={withLinkProps.url}>Superlink</RouterLink>;
};
I have a list of items, in a table in React. When I click on the table Link, the url updates, but the component doesn't get rendered. If I refresh the component is there.
I have read that some React-router-dom versions have some problems, and there are a lot of solutions that are being discussed here:
https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/guides/blocked-updates.md
That although is for the Beta Versions. I assume I need to do something with the higher order function withRouter from react-router-dom, but it doesn't work for me. On top of that, withRouter has been set up globally in the App.
const AppHeader = withRouter(({ history, location, ...props }) => (
<Header {...props} currentRoute={location.pathname} />
));
The thing is, my App has several applications. One Angular and 2 React ones. Do I need to set it up, on each application as well? This is the Parent Component with the router for the application the Links aren't working.
import React from 'react';
import { Route, Switch, Redirect } from 'react-router-dom';
import { connect } from 'react-redux';
import 'react-tagsinput/react-tagsinput.css';
import PolicyRoutes from 'dataprotection/policies/containers/PolicyRoutes';
import './styles.scss';
const AppRouter = () => (
<Switch>
<Route exact path="/policies" component={PolicyRoutes} />
<Redirect to="/policies" />
</Switch>
);
AppRouter.propTypes = {};
function mapStateToProps() {
return {};
}
export default connect(mapStateToProps)(AppRouter);
I have tried 2 solutions. The first is to actually wrap my component withRouter like so:
<Route exact path="/policies" component={withRouter(PolicyRoutes)} />
And the second is to wrap withRouter, the connect function. Both aren't working.
This is the component not working:
import React from 'react';
import { Route, Switch, Redirect } from 'react-router-dom';
import PropTypes from 'prop-types';
import OverviewScreen from '../OverviewScreen';
import PolicyDetailsScreen from '../PolicyDetailsScreen';
const PolicyRoutes = ({ match }) => (
<Switch>
<Route exact path={`${match.url}/details/:policyId`} component={PolicyDetailsScreen} />
<Route exact path={match.url} component={OverviewScreen} />
<Redirect to={match.url} />
</Switch>
);
PolicyRoutes.propTypes = {
match: PropTypes.object
};
export default PolicyRoutes;
Can anyone help? I don't know what the problem is...
I'm designing a website that just like facebook or anyother website that I seen I assume works (disclosure: I'm new to web programing), what I want to do is to route to different routes but in one of my routes or possibly even more I need to pass info to the next screen route when I to the new page for example: (I'm in
www.website.com/page1 then move to www.website.com/page1/page2) whilst passing data through the state say I want to pass a date or a name but I would not want it to be shown in url. So I found that react can pass with:
<Link {to={pathname:"/page2", state:{statetopass:datatopass}}}>
However,when I do pass the state once I'm in (www.website.com/page1/page2) I'm unable to read the data only when I refresh which i find weird will I ever see the data passed, I read that history is mutable but I can't really understand what that means its probably something to do with what my problem is.
The code that I have tried so far is here:
<-------------------- APP--------------------------->
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Switch, IndexRoute, HashRouter } from 'react-router-dom';
import Page1 from './Page1'
import Page2 from './Page2'
import { createBrowserHistory } from "history";
const history = createBrowserHistory();//idk if history should be here seems
class App extends Component {//to make no difference
constructor(props) {
super(props);
this.state = {
}
}
render() {
return (
<Router>
<Switch>
<Route exact path='/hom/Page1' component={({ match }) => { return (<Page1 />) }} />
<Route exact path='/hom/Page1/Page2' component={({ match }) => { return (<Page2 />) }} />
</Switch>
</Router>
)
}
}
export default App;
<--------------------Page1----------------->
import React, { Component } from 'react';
import { Link } from 'react-router-dom'
class Page1 extends Component {
constructor(props) {
super(props);
this.state = {}
}
render() {
return (
<div>
<Link to={{
pathname: `Page1/Page2`,
state: { dataneed: "testme" }
}}><button>Check </button>
</Link>
</div>
)
}
}
export default Page1;
<-------------------------------Page2----------------------------->
import React, { Component } from 'react';
import { createBrowserHistory } from "history";
const history = createBrowserHistory();
class Page2 extends Component {
constructor(props) {
super(props);
this.state = {}
}
render() {
console.log(history.location.state.dataneed)
return (
<div>
<h1>{history.location.state.dataneed}</h1>
</div>
)
}
}
export default Page2;
So you will see that at first you get an error but then once you refresh you see the text being displayed. If anyone could suggest the best way to go about doing whta I'm trying and if anyone could help me shed some light on the matter I would greatly appreciate this.
PS: I'm using 4.3.1 version there are videos out there but those seem to use version lower than 4.0 and completely different.
I believe the issue here is the mix of React Router and the History package. React Router uses History and has a history built in to its routers, so there is no need to explicitly use createBrowserHistory or anything from History directly. Specifically the issue is that the state is passed to the Link, from React Router, but then you attempt to access the data from the createBrowserHistory() object.
What you can do to resolve this issue and keep your code a bit cleaner is basically not use createBrowserHistory directly and instead rely on the built-in history from React Router. The routing data can be accessed through props.location, props.history, and props.match, which are injected into any component wrapped in the higher-order component withRouter from React Router.
What this will look like:
In index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
ReactDOM.render((
<BrowserRouter>
<App/>
</BrowserRouter>
), document.getElementById('root'));
In App.js:
import React, { Component } from 'react';
import { Route, Switch, withRouter } from 'react-router-dom';
import Page1 from './Page1'
import Page2 from './Page2'
class App extends Component {
constructor(props) {
super(props);
this.state = {
}
}
render() {
return (
{/* Note no need for a Router because we wrapped the App component in the BrowserRouter in index.js */}
<Switch>
{/* Note the component attribute can be simplified */}
<Route exact path='/hom/Page1' component={ Page1 } />
<Route exact path='/hom/Page1/Page2' component={ Page2 } />
</Switch>
)
}
}
export default withRouter(App);
Page1 is fine.
In Page2:
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
class Page2 extends Component {
constructor(props) {
super(props);
this.state = {}
}
render() {
const { dataneed } = this.props.location.state;
return (
<div>
<h1>{ dataneed }</h1>
</div>
)
}
}
export default withRouter(Page2);
Hopefully this helps, let me know if you have questions!
Trying to figure out how can I go back to the previous page. I am using [react-router-v4][1]
This is the code I have configured in my first landing page:
<Router>
<div>
<Link to="/"><div className="routerStyle"><Glyphicon glyph="home" /></div></Link>
<Route exact path="/" component={Page1}/>
<Route path="/Page2" component={Page2}/>
<Route path="/Page3" component={Page3}/>
</div>
</Router>
In order to forward to subsequent pages, I simply do:
this.props.history.push('/Page2');
However, how can I go back to previous page?
Tried few things like mentioned below but no luck:
1. this.props.history.goBack();
Gives error:
TypeError: null is not an object (evaluating 'this.props')
this.context.router.goBack();
Gives error:
TypeError: null is not an object (evaluating 'this.context')
this.props.history.push('/');
Gives error:
TypeError: null is not an object (evaluating 'this.props')
Posting the Page1 code here below:
import React, {Component} from 'react';
import {Button} from 'react-bootstrap';
class Page1 extends Component {
constructor(props) {
super(props);
this.handleNext = this.handleNext.bind(this);
}
handleNext() {
this.props.history.push('/page2');
}
handleBack() {
this.props.history.push('/');
}
/*
* Main render method of this class
*/
render() {
return (
<div>
{/* some component code */}
<div className="navigationButtonsLeft">
<Button onClick={this.handleBack} bsStyle="success">< Back</Button>
</div>
<div className="navigationButtonsRight">
<Button onClick={this.handleNext} bsStyle="success">Next ></Button>
</div>
</div>
);
}
export default Page1;
I think the issue is with binding:
constructor(props){
super(props);
this.goBack = this.goBack.bind(this); // i think you are missing this
}
goBack(){
this.props.history.goBack();
}
.....
<button onClick={this.goBack}>Go Back</button>
As I have assumed before you posted the code:
constructor(props) {
super(props);
this.handleNext = this.handleNext.bind(this);
this.handleBack = this.handleBack.bind(this); // you are missing this line
}
UPDATED:
Now we have hook, so we can do it easily by using useHistory
const history = useHistory()
const goBack = () => {
history.goBack()
}
return (
<button type="button" onClick={goBack}>
Go back
</button>
);
ORIGINAL POST:
this.props.history.goBack();
This is the correct solution for react-router v4
But one thing you should keep in mind is that you need to make sure this.props.history is existed.
That means you need to call this function this.props.history.goBack(); inside the component that is wrapped by < Route/>
If you call this function in a component that deeper in the component tree, it will not work.
EDIT:
If you want to have history object in the component that is deeper in the component tree (which is not wrapped by < Route>), you can do something like this:
...
import {withRouter} from 'react-router-dom';
class Demo extends Component {
...
// Inside this you can use this.props.history.goBack();
}
export default withRouter(Demo);
For use with React Router v4 and a functional component anywhere in the dom-tree.
import React from 'react';
import { withRouter } from 'react-router-dom';
const GoBack = ({ history }) => <img src="./images/back.png" onClick={() => history.goBack()} alt="Go back" />;
export default withRouter(GoBack);
Each answer here has parts of the total solution. Here's the complete solution that I used to get it to work inside of components deeper than where Route was used:
import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'
^ You need that second line to import function and to export component at bottom of page.
render() {
return (
...
<div onClick={() => this.props.history.goBack()}>GO BACK</div>
)
}
^ Required the arrow function vs simply onClick={this.props.history.goBack()}
export default withRouter(MyPage)
^ wrap your component's name with 'withRouter()'
Here is the cleanest and simplest way you can handle this problem, which also nullifies the probable pitfalls of the this keyword. Use functional components:
import { withRouter } from "react-router-dom";
wrap your component or better App.js with the withRouter() HOC this makes history to be available "app-wide". wrapping your component only makes history available for that specific component``` your choice.
So you have:
export default withRouter(App);
In a Redux environment export default withRouter( connect(mapStateToProps, { <!-- your action creators -->})(App), ); you should even be able to user history from your action creators this way.
in your component do the following:
import {useHistory} from "react-router-dom";
const history = useHistory(); // do this inside the component
goBack = () => history.goBack();
<btn btn-sm btn-primary onclick={goBack}>Go Back</btn>
export default DemoComponent;
Gottcha useHistory is only exported from the latest v5.1 react-router-dom so be sure to update the package. However, you should not have to worry.
about the many snags of the this keyword.
You can also make this a reusable component to use across your app.
function BackButton({ children }) {
let history = useHistory()
return (
<button type="button" onClick={() => history.goBack()}>
{children}
</button>
)
}```
Cheers.
Can you provide the code where you use this.props.history.push('/Page2');?
Have you tried the goBack() method?
this.props.history.goBack();
It's listed here https://reacttraining.com/react-router/web/api/history
With a live example here https://reacttraining.com/react-router/web/example/modal-gallery
If using react hooks just do:
import { useHistory } from "react-router-dom";
const history = useHistory();
history.go(-1);
UPDATE 2022 w V6
navigate(-1)
to omit the current page from history:
navigate(-1, { replace: true })
Try:
this.props.router.goBack()
Simply use
<span onClick={() => this.props.history.goBack()}>Back</span>
Hope this will help someone:
import React from 'react';
import * as History from 'history';
import { withRouter } from 'react-router-dom';
interface Props {
history: History;
}
#withRouter
export default class YourComponent extends React.PureComponent<Props> {
private onBackClick = (event: React.MouseEvent): void => {
const { history } = this.props;
history.goBack();
};
...
Maybe this can help someone.
I was using history.replace() to redirect, so when i tried to use history.goBack(), i was send to the previous page before the page i was working with.
So i changed the method history.replace() to history.push() so the history could be saved and i would be able to go back.
I am not sure if anyone else ran into this problem or may need to see this. But I spent about 3 hours trying to solve this issue:
I wanted to implement a simple goBack() on the click of a button. I thought I was off to a good start because my App.js was already wrapped in the Router and I was importing { BrowserRouter as Router } from 'react-router-dom'; ... Since the Router element allows me to assess the history object.
ex:
import React from 'react';
import './App.css';
import Splash from './components/Splash';
import Header from './components/Header.js';
import Footer from './components/Footer';
import Info from './components/Info';
import Timer from './components/Timer';
import Options from './components/Options';
import { BrowserRouter as Router, Route } from 'react-router-dom';
function App() {
return (
<Router>
<Header />
<Route path='/' component={Splash} exact />
<Route path='/home' component={Info} exact />
<Route path='/timer' component={Timer} exact />
<Route path='/options' component={Options} exact />
<Footer />
</Router>
);
}
export default App;
BUT the trouble was on my Nav (a child component) module,
I had to 'import { withRouter } from 'react-router-dom';'
and then force an export with:
export default withRouter(Nav);
ex:
import React from 'react';
import { withRouter } from 'react-router-dom';
class Nav extends React.Component {
render() {
return (
<div>
<label htmlFor='back'></label>
<button id='back' onClick={ () => this.props.history.goBack() }>Back</button>
<label htmlFor='logOut'></label>
<button id='logOut' ><a href='./'>Log-Out</a>
</button>
</div>
);
}
}
export default withRouter(Nav);
in summary, withRouter was created because of a known issue in React where in certain scenarios when inheritance from a router is refused, a forced export is necessary.
You can use history.goBack() in functional component. Just like this.
import { useHistory } from 'react-router';
const component = () => {
const history = useHistory();
return (
<button onClick={() => history.goBack()}>Previous</button>
)
}