React Router v4: Prevent navigating back - reactjs

Im building this app where a user can book an appointment. Its a multistep process so the users makes some choices along some routes, lets call them "/route1", "/route2", "route3" and finally after the booking is completed they get navigated to a "/confirmation" route where they can see all the details of their booking.
If the user presses the back button on the browser at this stage, I want to either redirect them to the index page or rather first display a confirm message telling them that they can't go back at this point, and if they still want to, they will get navigated to index page.
I saw that react-router has a Prompt component, but I cant configure it to navigate to a certain path. It only allows the user to either stay, or continue the navigation.
Any tips for how to handle this situation?

React prompt have parametr 'when' that makes Prompt action conditional. You can set boolean value in state (if true - Prompt active), something like this
class MyComponent extend React.Component {
constructor(props) {
super(props);
this.state = {
isNavigationBlocked: false;
}
}
render(){
return (<div>
<Prompt when={this.state.isNavigationBlocked}/>
</div>)
}
}
And change this state in some handler.

Related

Preserve internal state on page refresh in React.js

It must be pretty regular issue.
I'm passing props down to the children and I'm using it there to request to the endpoint. More detailed: I'm clicking on the list item, I'm checking which item was clicked, I'm passing it to the child component and there basing on prop I passed I'd like to request certain data. All works fine and I'm getting what I need, but only for the first time, ie. when refreshing page incoming props are gone and I cannot construct proper URL where as a query I'd like to use the prop value. Is there a way to preserve the prop so when the page will be refresh it will preserve last prop.
Thank you!
(You might want to take a look at: https://github.com/rt2zz/redux-persist, it is one of my favorites)
Just like a normal web application if the user reloads the page you're going to have your code reloaded. The solution is you need to store the critical data somewhere other than the React state if you want it to survive.
Here's a "template" in pseudo code. I just used a "LocalStorage" class that doesn't exist. You could pick whatever method you wanted.
class Persist extends React.Component {
constuctor(props) {
this.state = {
criticalData = null
}
}
componentDidMount() {
//pseudo code
let criticalData = LocalStorage.get('criticalData')
this.setState({
criticalData: criticalData
})
}
_handleCriticalUpdate(update) {
const merge = {
...LocalStorage.get('criticalData')
...update
}
LocalStorage.put('criticalData', merge)
this.setState({
criticalData: merge
})
}
render() {
<div>
...
<button
onClick={e => {
let update = ...my business logic
this._handleCriticalUpdate(update) //instead of set state
}}
>
....
</div>
}
}
By offloading your critical data to a cookie or the local storage you are injecting persistence into the lifecycle of the component. This means when a user refreshes the page you keep your state.
I hope that helps!

Handle back button with react router

If a user navigates to www.example.com/one and clicks the back button, I want to redirect them to www.example.com.
I think it's a common problem, but I haven't found a solution yet.
Hooks version (React 16.8+):
Minimal version.
import { useHistory } from "react-router-dom";
export const Item = () => {
let history = useHistory();
return (
<>
<button onClick={() => history.goBack()}>Back</button>
</>
);
};
In react-router-dom v6 useHistory() is replaced by useNavigate(). so use useNavigate() inplace of useHistory() this way.
import { useNavigate} from "react-router-dom";
export const Item = () => {
let navigate = useNavigate();
return (
<>
<button onClick={() => navigate(-1)}>Back</button>
</>
);
};
for more on useNavigate visit this: https://reactrouter.com/docs/en/v6/hooks/use-navigate
You can try with two options, either you can use push method or goBack method from history of the router. Normally history props will available if you directly route the component via Route method or pass the history props to child component and use it.
Sample Code given below
this.props.history.push('/') //this will go to home page
or
this.props.history.goBack() //this will go to previous page
For your problem you try with push method and give the exact url you to move on.
For more reference visit https://reacttraining.com/react-router/web/api/history
What you want is this:
Let's say a person goes to a single page in your website such as: www.yoursite.com/category/books/romeo-and-juliet
In this page, you want to show a "Back" button that links you to one upper directory which is: www.yoursite.com/category/books/
This is breadcrumb system that we famously had in vBulletin forums and such.
Here is a basic solution to this:
let url = window.location.href;
let backButtonUrl = "";
if (url.charAt(url.length - 1) === "/") {
backButtonUrl = url.slice(0, url.lastIndexOf("/"));
backButtonUrl = backButtonUrl.slice(0, backButtonUrl.lastIndexOf("/"));
} else {
backButtonUrl = url.slice(0, url.lastIndexOf("/"));
}
What it basically does is:
1. Get the current URL from browser
2. Check if there is a "/" (slash) at the end of the link.
a. If there is: remove the slash, and remove everything the last slash
b. If there is not: remove everything last slash.
You can use {backButtonUrl} as your Go Back button link.
Note: it does not have anything to do with React Router, history, etc.
Note 2: Assuming you are using a link architecture that goes like www.site.com/word/letter/character
If you want to use it with react-router-dom library, then you need to set your url variable like this:
let url = this.props.match.url;
I found a solution. It's not beautiful but it works.
class Restaurant extends Component {
constructor(props) {
super(props);
this.props.history.push('/');
this.props.history.push(this.props.match.url);
}
...
I've had the same problem today. I have the following flow in one of the applications I'm working on:
User fills out a registration form
User enters credit card "payment page"
When payment is successful, the user sees a "payment confirmation" page.
I want to prevent the users from navigating from the "payment confirmation" (3) page back to any previous steps in the payment flow (1 and 2).
The best thing to do would be not to use routes to control which content is displayed, and use state instead. If you cannot afford to do that,
I found two practical ways to solve the problem:
Using React-Router:
When you hit the back button, React Router's history object will look like this:
When you go to any page using history.push record the page you are visiting in the state
Create a decorator, HOC, or whatever type of wrapper you prefer around the React-Router's Route component. In this component: If history.action === "POP" and "history.state.lastVisited === <some page with back navigation disabled>", then you should redirect your user to the /home page using history.replace
Another way to do is is by going to the /home page directly:
Use history.action to detect the back button was used, then:
Change location.href to equal the page you want to visit
Call location.reload(). This will reload the application, and the history will be reset
Browser back button works on your routes history. It will not invoke your programmatically handled routing. That's the point where we should keep maintain history stack with react router. If you are at route '/' and push '/home'. On browser back button it will pop '/home and will go back to '/'.
Even If you implementButton component for go back functionality and use react router history props. Believe me you have to carefully manage your navigation to maintain browser history stack appropriately. So it behaves same like whether you press browser back button or your app Button to go back or go forward.
I hope this would be helpful.
We wanted something similar for our React app and unfortunately this was the best solution we came up with. This is particularly helpful when our users are on mobile devices and they land on a specific page on our site from an ad or a referrer.
This is in our main routes.tsx file.
useEffect(() => {
// The path that the user is supposed to go to
const destinationPath = location.pathname;
// If our site was opened in a new window or tab and accessed directly
// OR the page before this one was NOT a page on our site, then...
if (
document.referrer === window.location.href ||
document.referrer.indexOf(window.location.host) === -1
) {
// Replaces the current pathname with the homepage
history.replace("/");
// Then pushes to the path the user was supposed to go to
history.push(destinationPath);
}
}, []);
Now when a user presses the back button, it takes the user to our homepage instead of being stuck within the "nested" route they were in.
NOTE: There are some small quirks with this implementation. Our app is also a Cordova app so we NEED to have our own back button. This implementation works well with our own back button but does not seem to work with the native browser's back button; hence, it worked well for our needs.

How to define state?

Does anyone have a good definition for state in the context of a web app?
And more specifically: what does state mean in the context of React - does that differ at all from the first definition?
I see the term state being used a lot in React development but I haven't been able to find a solid, concise definition for it.
State in the context of both cases (react and web apps) is the same.
From wikipedia
In information technology and computer science, a program is described as stateful if it is designed to remember preceding events or user interactions; the remembered information is called the state of the system.
The important part of that quote is remember preceding events or user interactions.
In a web app
State is typically stored in a database somewhere. The web app retrieves the 'state' (data) from the database, presents a view that allows the user to interact with the state, then sends the new 'state' (data) back to the database.
In react
React can be thought of as presenting the 'state' of an application to the user. Data is retrieved from somewhere, react displays the data (state) to the user, allows the user to modify it, and then sends it back to where it found it (remembering).
However, when people talk about 'state' in the context of react, they are generally referring to the internal representation of the data or interactions that react is holding in memory while the user is busy interacting with it.
A simple react component that holds some state:
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {
userName: 'Leeloo'
};
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
const name = (this.state.userName === 'Leeloo') ? 'Korben' : 'Leeloo'
this.setState({
userName: name
})
}
render() {
return ( <
button onClick = {
this.handleClick
} > {
this.state.userName
} <
/button>
);
}
}
ReactDOM.render( < Toggle / > , document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
In the example above, the component creates some state and stores it in a 'state' property of the class.
It is remembering it's internal state.
When the component renders, it looks up the value stored in state and displays it on the button label. When the button is clicked, this.state is updated to 'remember' the event of clicking the button.
In a full featured web app, you would be retrieving data from a database, storing that data in state, allowing the user to interact with it, then sending that data back to the database.
For example, you might display a user profile page, the user changes their name, password, description, etc... You would store the 'state' of all the changes they made on that page until they click a submit button. Then you could gather up all the changes from the components state and send it back to the database for storage (remembering).
Also, you may want to store state in a react component to describe how the appearance of the app should be based on interactions with it. For example, an InputBox component may have a hasError state and when true, adds a red border to the component.
General: State is all data currently stored by the application.
In context of React: State is an object that defines - besides props - how a component is rendered. State can be (unlike props) changed by the component itself.

Whats the right way to redirect when data is not found with Redux and Router?

I've got a redux state like this:
{
currentItemId: 1,
items: [{id: 1, name: 'todo 1'}, ...]
}
When currentItemId is set, the app displays the item's detail.
Now in a rare case when the currentItem is set to ID that doesn't represent an item in items array, I want to redirect back.
A good place to catch that seems the connect of our ItemForm, where I can issue a redirect.
The problem is, that it complains about calling setState and maybe all I need to do is to schedule it for later.
I also want to make sure I don't show the ItemForm in case the item could not be found.
Whats the right thing to do in this scenario?
How you redirect differs slightly depending on which routing method you are using. Assuming that you are using react-router-dom I would just do a redirect either in render or componentDidMount. Assuming that you map your currentItemId to a prop for your component you could do something like the following using withRouter HOC.
componentDidMount() {
if(!this.props.currentItemId) { // Or whatever value you want to check for
this.props.history.push('/redirectpageurl');
}
}
Another way to do redirects in react-router-dom is to render a Redirect component.
render() {
if(!this.props.currentItemId) {
return <Redirect to="/redirectpageurl" />
}
// ...
}

react router navigate links from rest api

I'm new in react and faced with the problem when tried to accomplish navigate through my api pages. What I've done: http://pastebin.com/hxf9PJ8k
It works perfectly on my opinion, but show only first api page. Links to all user profile works too, it was simple to do just adding an id to api link. So, my api returned to me objects that is actually users, and overall count of users in database and links to next page if exist or null and previous page or null. Now I cannot understand how to use those link to create 2 buttons — next and prev. How to pass whole link or just part next to page= to Users props and make xhr request.
Api example view:
count: 2302
next: "http://localhost:8080/instant/api/?page=2"
previous:
results:
0: Object
1: Object
2: Object
Thanks in advance!
Basically what suggest to yo to do is to create a currentPage variable which makes your logic very simple.
for previous and next buttons create a separate component to handle paging condition, handle any click event on the parent component to keep it simple.
class PrevBtn extends React.Component {
constructor(props){
super(props);
this.props.clickHandler = this.props.clickHandler.bind(this);
}
render() {
let button = null;
const {currentPage} = this.props;
if(currentPage>1){
button = <a onClick={this.props.clickHandler}>Prev</a>
}else{
button = <a disabled="disabled">Prev</a>
}
return(<div>{button}</div>)
}
}
I just did an example for you in codePen, but I just did the prev button logic, you can figure it out how to do the next button component.

Resources