Problem description
The project stack is React +ant-design;
Product requirements: When information is entered or changed on a page form and the user does not save the information to leave the page, the user can customize the prompt. If the user confirms to leave, the user will jump to a new page; otherwise, the user will stay on the current page.
thinking
In the React single-page application, users need to decide whether to redirect the page based on their choice. That is, they need to block the page redirect of the browser. This is similar to the return false effect after a label is clicked.
First, you can think of the browser’s window.confirm method, which is perfectly interactive, but what about custom requirements? After consulting the data, it was found that the current mainstream browsers do not support customization of this method style, so it is abandoned.
In React, react-Router controls page routing. It would be better if the authorities provided this capability. IO /react-route/react-guide.github. , or reacttraining.com/react-route… And the Chinese version of segmentfault.com/a/119000001…
Key documents
As used in the project is the react – the router – v4, so directly to view the latest API (if is older version has a relatively simple implementation, can consult blog.csdn.net/ISaiSai/art…).
Prompt component
import { Prompt } from 'react-router-dom'; .render() {return(
<Prompt message="Sure you want to leave? when={true}} / >)Copy the code
In the code above, you can use the React-router-dom Prompt, which can be used in any JSX render method (not just in the Router component), and will Prompt the user when the current page component leaves. The default pop-up box is the browser window.confirm.
- Message: string Prompt message set when the user leaves the current page.
- message: Func Callback function set when the user leaves the current page,
(Are you Sue you want to go to ${location. Pathname}?) } / > - When: bool Whether Prompt is enabled or not is determined by setting certain conditions. Prevent conversion is enabled when the property value is true.
The getUserConfirmation method of the Router component
The function used to confirm the navigation, using window.confirm by default. For example, when you navigate from/A to/B, the default confirm function is used to display a prompt. The user must click OK to navigate. Otherwise, no action is performed. Need to be used in conjunction with
const getConfirmation = (message, callback) => {
const allowTransition = window.confirm(message);
callback(allowTransition);
}
<BrowserRouter getUserConfirmation={getConfirmation} />
Copy the code
Implementation scheme
Main idea: block the page and wait for the router change to be processed asynchronously after user operation.
Exploration 1: Since Prompt is a component, is it possible to render with state changes within components?
Try the following code:
. <Prompt message = {(location)=>{return this.state.customPromt,
}
}
/>
...
Copy the code
This does not work. The Prompt component’s message method is called synchronously and does not block the page.
Exploration 2: Try async/await
Since we are going to execute asynchronously, we can try async/await asynchronous processing and write synchronously. It just so happens that message can also be a function. So the new try code is as follows:
. handlePrompt = async () => { const leaveConfirm = new Promise((res, rej) => { confirm({ content:'Are you sure? '.onOk() {
res(true);
},
onCancel() {
res(false); }}); }); const leave = await leaveConfirm;returnleave ; }... <Prompt message={(location) => {this.handlePrompt} /> ...Copy the code
With high expectations, and then… Still no. The message method returns a string or true. If the string is returned, the default prompt method is invoked to block the page, which is the same as window.prompt. Return true to jump directly.
message: func
Will be called with the next location and action the user is attempting to navigate to. Return a string to show a prompt to the user or true to allow the transition.
The above attempt code, although added asynchronous method, will also execute asynchronous function, but will always jump to the page, after checking the reason is: github.com/ReactTraini…
Exploration 3: getUserConfirmation method,Github.com/ReactTraini…
The getUserConfirmation method is provided for customization. By default, when a page uses the Prompt component, The getUserConfirmation method called is the browser’s default window.prompt. If you need to customize, directly overwrite.
A simple example code is as follows:
const getConfirmation = (message, callback) => {
const allowTransition = window.confirm(message);
callback(allowTransition);
}
<BrowserRouter getUserConfirmation={getConfirmation} />
Copy the code
Ps: Note that this method needs to be written on BrowserRouter or MemoryRouter.
The next question, then, is to incorporate custom or other UI components into this method. The implemented code is as follows:
import { Modal } from 'antd'; .functionGetConfirmation (message, callback) {// The crucial callback method, which can be executed asynchronouslyif(! G.pagechangeconfirm) {// g.pagechangeconfirm is a global variable in the page, used for data interaction and conditional judgment callback(true);
return;
}
Modal.confirm({
title: 'When you leave this page, the form information will not be retained? Are you sure to leave this page? ',
content: ' ',
okText: 'leave',
cancelText: 'cancel'.onOk() {
callback(true);
},
onCancel() {
callback(false); }}); } ReactDOM.render(( <BrowserRouter getUserConfirmation={getConfirmation} > <App /> </BrowserRouter> , document.getElementById('react-wraper')); .Copy the code
Explore 4: Check GitHub issues for other solutions
Spent half a day time, found this post to https://github.com/ReactTraining/react-router/issues/4635 in the issue, interested can look at the discussion process. There are two solutions mentioned:
-
GetUserConfirmation, similar to the solution above me, can run the complete reference code: codepen. IO/PSHRMN /pen/…
-
History. block, using history’s API, requires withRouter wrapping, portal: github.com/ReactTraini…