A preface
In front in the development process, there is a risk alarming developers, is under normal circumstances there is no problem, but because of one small online, or a server deployment, the online bugs, what is more online bug and online content irrelevant, so today I will give you share a real case.
The knowledge points covered by this case are as follows:
- 1 Specifications for installing dependency packages in the project.
- 2 Context consumption subscription.
- 3
react-router
V5.2.0 Version changed. - 4 Local and online accident investigation.
Ii. Background
Next, I would like to introduce the specific problem. Recently, a student (alias Xiao Ming) encountered an online accident caused by using the React-Router during development. This incident was caused by a global component that used custom hooks — useHistory from the React-router.
import { useHistory } from 'react-router'
function Index(){
/* Get the histroy object */
const history = useHistory()
console.log(history)
return <div>{/* Display the information in the history, expecting the component to update when the location information in the history changes */</div>
}
Copy the code
Ming uses the react-router useHistory to get the status of the history object. To:
- Show the fields in Location.
- When a route jumps, the history changes and the component Index is expected to be rerendered, updating the display.
This feature was never a problem in the project. But recently Ming developed a new feature that has nothing to do with the current component and went live.
The result is an online accident: when the route changes, the Index component is no longer updated as before.
What makes xiao Ming even more incredible is that he has no problems in his local environment. So the question was brought to the fore. This means that the problem only appears online.
This sudden question, let Xiao Ming a face meng force, immediately panic hands and feet. So what causes it?
Three Solving problems
Local and online are different
Now let’s help Xiao Ming solve this problem. So first of all 🤔 think about: why does the local and online inconsistency occur?
In this case, the first thing to think about is whether the online dependency package is different from the local one. So the verification is also very simple, is to upgrade all packages locally, because the online deployment of packages, generally a new package is installed. Then you can verify this in the following way:
- Download the local
node_modules
; - reinstall
npm install
After the above scheme twists and turns, the local phenomenon is the same as the online one. This raises a new question. Ming has never updated project dependencies at all, so why the difference in dependency packages?
If your project relies on the X.X.X module, will the X.X.X module be installed in the project? The answer is no. How to deal with NPM in detail will be introduced in a moment. The react-Router library is the main source of the problem, so let’s look at package.json in The Project
"react-router": "^ 5.1.2." ".Copy the code
- As you can see above, Xiaoming used it in the project
react-router
是5.1.2
Version of theta, so the problem is theta.
NPM version installation mechanism
^ What does it mean in package.json? Originally in package.json ^ will match the latest large version of the dependency package. For example:
- If you rely on the version, write it this way
^ 1.2.3
, indicates that the latest version of 1.x.x (no later than 1.2.3, including 1.3.0) is installed, but 2.x.x is not installed. That is, the major version number is not changed during installation.
If there are higher versions of the React-Router such as 5.2.x or 5.3.x, then the latest installation package will be downloaded up to 6.0.0.
It is important to note that if the major version number is 0, the behavior of the insertion number is the same as that of the tilde. This is because at this stage of development, even minor version number changes can cause incompatibilities in the program. (Main version)
For example ^0.2.3, the installed version range is >=0.2.3 <0.3.0.
Depends on version mapping
symbol | example | The scope of | instructions |
---|---|---|---|
^ Will match the latest large version dependency package | ^ 1.2.3 | > = 1.2.3 < 2.0.0, | X.x indicates that the latest version of 1.x.x (no later than 1.2.3, including 1.3.0) is installed, but 2.x.x is not installed, that is, the major version number is not changed during installation. |
~ will match the most recent minor version dependency package | ~ 1.2.3 | > = 1.2.3 < 1.3.0 | The latest version of 1.2.x (no less than 1.2.3) is installed, but 1.3.x is not installed, that is, the major and minor versions are not changed during installation. |
> = | > = 2.1.0 | > = 2.1.0 | The value is greater than or equal to 2.1.0 |
< = | < = 2.0.0 | < = 2.0.0 | Less than or equal to 2.0.0 |
laster | — | — | Install the latest version |
* | — | — | Any version |
– | 1.2.3-2.3.4 | > = 1.2.3 < = 2.3.4 | Between two versions |
So let’s go back to the problem xiao Ming encountered, now that we know that the cause is automatic upgrade, so how to solve the problem?
Now it’s time to fix the problem. If there are bugs caused by differences between the online and local versions, the most direct and quick way is to fix the version.
"react-router": "5.1.2".Copy the code
The version number does not add any symbol, fixed version 5.1.2, the most fundamental and effective solution to the problem.
The react-Router cannot use useHistory to subscribe to routing information. So what exactly has changed? React-Routerv5.1.2
export function useHistory() {
return useContext(Context).history;
}
Copy the code
- As you can see above
useHistory
It’s essentially calleduseContext
, using the entire routing libraryContext
çš„history
Object. - Context stores the entire route state information, and every time a route changes, it notifies the routing component to render the corresponding view through the Context change. The React-Router Router is a Router that can be routed from a Router to another Router
If you’re not familiar with context’s subscription consumption mechanism, go to 👇.
Context consumption mechanism
UseHistory essentially uses useContext, which is essentially subscribed to a new version of the React Context object. The React Context subscription update mechanism is necessary.
The new version of the Context object includes the Provider and the subscriber Consumer:
Provider
: Passes the context value.Consumer
: Consumes the value provided by the Provider.- Class components
contextType
And function componentsuseContext
You can subscribe to consume the context value, and when the context value changes, they’re rerendered, and they’re not affectedPureComponent
,memo
,shouldComponentUpdate
The impact of optimization strategies.
Let’s go back to the problem Ming encountered. Before Ming used useHistory to subscribe to route changes. When the route is updated, the component using useHistory will be re-rendered because the logic was that route updates will update the history object. Let’s simulate the process.
const Context = React.createContext()
function useName (){
return React.useContext(Context).name
}
const Child = () = >{
const name = useName()
return <div>
{name}
</div>
}
const Index = memo(function(){
return <div>
<p>The root component</p>
<Child/>
</div>
})
export default function App(){
const [ value , changeValue ] = React.useState({ name:'list' , path:'/list' })
return <div>
<Context.Provider value={value} >
<Index />
</Context.Provider>
<button onClick={()= >ChangeValue ({name:' home ',path:'/detail'})} > changeValue</button>
</div>
}
Copy the code
Effect:
- Switching routes is equivalent to calling
changeValue
Changed,Provider
In thevalue
. - The component xiao Ming uses is Child, and the
useHistory
Similar to theuseName
. - Change value when clicking the button. Child updates the view.
The react to the router
Context subscription update mechanism, so why useHistory, the new version of react-router changes? In the react-Router v5.2.0, the history Context has been removed and the history Context has been created.
Here’s the Releases record:
Then we went to look at the source code:
export function useHistory() {
return useContext(HistoryContext);
}
export function useLocation() {
return useContext(Context).location;
}
Copy the code
From the above you can see:
- UseHistory is no longer subscribed
Context
, butHistoryContext
. - UseLocation still subscribes
Context
. - When we change the route, essentially what we change is
Context
, so useuseLocation
The components will be updated to useuseHistory
Components are not updated.
Here comes the revelation, and the truth finally emerges.
Four summarizes
Through the study of this paper, we can gain the following contents:
- Troubleshoot online and local inconsistency.
package.json
The version number is incorrect.- UseHistory principle.
- Context Subscription update process.
Hope that the students who feel harvest can give the author praise + attention, continue to share front-end good articles.
The resources
- Package. Json explanation
- The react-Router routing principle was thoroughly understood