facebook / facebook/docusaurus

Progress bar isn't cleared when experimenting w/ strict-mode

Open
#12,321 2 comments 0 reactions 0 assignees View on GitHub
bug status: needs triage
Dominant language
TypeScript
Stars
66.2k
Forks
10k
Avg merge
1d 3h
Merged PRs (30d)
52

Description

### Have you read the Contributing Guidelines on issues?

- [x] I have read the [Contributing Guidelines on issues](https://github.com/facebook/docusaurus/blob/main/CONTRIBUTING.md#issues).

### Prerequisites

- [x] I'm using the latest version of Docusaurus.
- [x] I have tried the `npm run clear` or `yarn clear` command.
- [x] I have tried `rm -rf node_modules yarn.lock package-lock.json` and re-installing packages.
- [x] I have tried creating a repro with https://new.docusaurus.io.
- [ ] I have read the console error message carefully (if applicable).

### Description

Hi!

Firstly, thanks for your work on this project! 🙂

And also, I am sorry for opening this PR (https://github.com/facebook/docusaurus/pull/12310) first without asking you.

Today I used [patch-package](https://github.com/ds300/patch-package) to patch `@docusaurus/core@3.10.2` for the project I'm working on.

While I was experimenting docusaurus w/ strict-mode by adding this into 'src/theme/Root.js':

```js
export { StrictMode as default } from "react";
```

during dev-mode, the progress-bar is still displayed even if the location transition was finished.

video here:

https://github.com/user-attachments/assets/d95d04a5-1baa-4e85-8d64-a363ededa6f5

*I am putting this issue here so anyone (not just me) experimenting w/ strict-mode are aware of this issue.*

---

for other users experimenting w/ strict-mode and are having this issue -

Here is the diff that solved my problem (assuming you are using patch-package):

```diff
diff --git a/node_modules/@docusaurus/core/lib/client/PendingNavigation.js b/node_modules/@docusaurus/core/lib/client/PendingNavigation.js
index 977de49..4336d49 100644
--- a/node_modules/@docusaurus/core/lib/client/PendingNavigation.js
+++ b/node_modules/@docusaurus/core/lib/client/PendingNavigation.js
@@ -7,66 +7,74 @@
import React from 'react';
import { Route } from 'react-router-dom';
import ClientLifecyclesDispatcher, { dispatchLifecycleAction, } from './ClientLifecyclesDispatcher';
-import ExecutionEnvironment from './exports/ExecutionEnvironment';
import preload from './preload';
class PendingNavigation extends React.Component {
- previousLocation;
- routeUpdateCleanupCb;
+ routeUpdateCleanupCb = () => { };
+ isUnmounted = false;
constructor(props) {
super(props);
- // previousLocation doesn't affect rendering, hence not stored in state.
- this.previousLocation = null;
- this.routeUpdateCleanupCb = ExecutionEnvironment.canUseDOM
- ? dispatchLifecycleAction('onRouteUpdate', {
- previousLocation: null,
- location: this.props.location,
- })
- : () => { };
+ // Store renderedLocation in state so the old screen stays visible
+ // while the new route is being preloaded.
this.state = {
- nextRouteHasLoaded: true,
+ renderedLocation: props.location,
+ previousLocation: null,
};
}
- // Intercept location update and still show current route until next route
- // is done loading.
- shouldComponentUpdate(nextProps, nextState) {
- if (nextProps.location === this.props.location) {
- // `nextRouteHasLoaded` is false means there's a pending route transition.
- // Don't update until it's done.
- return nextState.nextRouteHasLoaded;
- }
- // props.location being different means the router is trying to navigate to
- // a new route. We will preload the new route.
- const nextLocation = nextProps.location;
- // Save the location first.
- this.previousLocation = this.props.location;
- this.setState({ nextRouteHasLoaded: false });
+ componentDidMount() {
this.routeUpdateCleanupCb = dispatchLifecycleAction('onRouteUpdate', {
- previousLocation: this.previousLocation,
- location: nextLocation,
+ previousLocation: null,
+ location: this.props.location,
});
- // Load data while the old screen remains. Force preload instead of using
- // `window.docusaurus`, because we want to avoid loading screen even when
- // user is on saveData
- preload(nextLocation.pathname)
- .then(() => {
+ }
+ // Intercept location updates in commit phase and still show old route
+ // until next route is done loading.
+ componentDidUpdate(prevProps) {
+ if (this.props.location !== prevProps.location) {
this.routeUpdateCleanupCb();
- this.setState({ nextRouteHasLoaded: true });
- })
- .catch((e) => {
- console.warn(e);
- // If chunk loading failed, it could be because the path to a chunk
- // no longer exists due to a new deployment. Force refresh the page
- // instead of just not navigating.
- window.location.reload();
- });
- return false;
+ // props.location being different means the router is trying to navigate
+ // to a new route. We will preload the new route.
+ const nextLocation = this.props.location;
+ this.routeUpdateCleanupCb = dispatchLifecycleAction('onRouteUpdate', {
+ previousLocation: prevProps.location,
+ location: nextLocation,
+ });
+ // Load data while the old screen remains. Force preload instead of using
+ // `window.docusaurus`, because we want to avoid loading screen even when
+ // user is on saveData
+ preload(nextLocation.pathname)
+ .then(() => {
+ if (this.isUnmounted) {
+ return;
+ }
+ this.routeUpdateCleanupCb();
+ this.setState({
+ renderedLocation: nextLocation,
+ previousLocation: prevProps.location,
+ });
+ })
+ .catch((e) => {
+ if (this.isUnmounted) {
+ return;
+ }
+ console.warn(e);
+ // If chunk loading failed, it could be because the path to a chunk
+ // no longer exists due to a new deployment. Force refresh the page
+ // instead of just not navigating.
+ window.location.reload();
+ });
+ }
+ }
+ componentWillUnmount() {
+ this.isUnmounted = true;
+ this.routeUpdateCleanupCb();
}
render() {
- const { children, location } = this.props;
+ const { children } = this.props;
+ const { renderedLocation, previousLocation } = this.state;
// Use a controlled to trick all descendants into rendering the old
// location.
- return (
- children}/>
+ return (
+ children}/>
);
}
}

```

*This issue body was [partially generated by patch-package](https://github.com/ds300/patch-package/issues/296).*

### Reproducible demo

https://github.com/seyoon20087/docusaurus-test-repro-1

### Steps to reproduce

*This only happens in development and does not suffer production usability; however I am still reporting this issue so that maintainers are aware of this issue. If you choose to clone the repo above, run `yarn install`, and skip to step 3.*
1. Create a new Docusaurus project
2. Add these contents to `src/theme/Root.js`: `export { StrictMode as default } from "react";`
3. Start the development server (`yarn start`)
4. Click any route. e.g. home -> blog, etc. Even after route transition has finished, the progress bar isn't cleared.

### Expected behavior

When route transition has finished, the progress bar should be cleared.

### Actual behavior

Even after route transition has finished, the progress bar isn't cleared.

Some other thing to note that cleanups aren't being run for the current route, (potentially?) resulting in this issue:

Image

### Your environment

- Public source code: https://github.com/seyoon20087/docusaurus-test-repro-1
- Public site URL: not relevant
- Docusaurus version used: 3.10.2
- Environment name and version (e.g. Chrome 89, Node.js 16.4): Chrome 152, Node.js 24.16.0 (though this would apply to all)
- Operating system and version (e.g. Ubuntu 20.04.2 LTS): macOS Ventura (though this would apply to all)

### Self-service

- [x] I'd be willing to fix this bug myself.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.