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-routerV5.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 localnode_modules;
  • reinstallnpm 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 projectreact-router 是 5.1.2Version 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 aboveuseHistoryIt’s essentially calleduseContext, using the entire routing libraryContext çš„ historyObject.
  • 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 componentscontextTypeAnd function componentsuseContextYou can subscribe to consume the context value, and when the context value changes, they’re rerendered, and they’re not affectedPureComponent ,memo , shouldComponentUpdateThe 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 callingchangeValueChanged,ProviderIn thevalue.
  • The component xiao Ming uses is Child, and theuseHistorySimilar 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 subscribedContext, butHistoryContext.
  • UseLocation still subscribesContext.
  • When we change the route, essentially what we change isContext, so useuseLocationThe components will be updated to useuseHistoryComponents 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.jsonThe 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