How to use history.push in HOC reactjs? - reactjs

I am having an axios intercept . It will catch every request . My idea is to dispatch errors commonly in the same place. So i created this intercept. If 404 error came means i will dispatch an action and redirect the page to home. But unfortunately i cant access the props.history in HOC.
Here i am sharing the code of what i have implemented:
HOC axios intercept:
import React, {useEffect} from "react";
import axios from "axios";
const checkRequests= Wrapped => {
function CheckRequests(props) {
useEffect(()=>{
axios.interceptors.response.use(function (response) {
// Do something with response data
return response;
}, function (error) {
switch (error.response.status) {
case 404 :
props.history.push('/login') // will redirect user to login page
break
default :
break
}
// Do something with response error
return Promise.reject(error);
});
})
return (
<Wrapped {...props} />
)
}
return CheckRequests
}
export default checkRequests
And wrapping this component in App.js:
import React from "react"
import CheckRequests from "./HOC/CheckRequests"
function App(props){ ... }
export default checkRequests(App)
Error:
When is console the props in HOC it comes empty
console.log(props) => {}
Please help me with that. Is there any other way to access the history.push from that HOC. For disptaching an action an action am using store.dispatch(logout()).

Wrap the withRouter HOC in the export statement like this:
export default withRouter(checkRequests);
Don't forget to import at the top of the file
import { withRouter } from "react-router";

You may use withRouter
import { withRouter } from 'react-router-dom';
export default withRouter(checkRequests);

Thanks for your answer. I added your suggestion into my code. But i got this error You should not use Route or withRouter() outside a Router . Then i found that it is outside the router so i added this
import { BrowserRouter } from 'react-router-dom';
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>
, document.getElementById('root'));
on the index.js . It works fine.

Related

How to properly wrap a Functional Component into a React HOC

I am building some web pages in nextjs and I need to make sure certain pages can only be accessed if the user has been authenticated as below:
import UserManager from "../managers/user_manager";
import { useRouter } from "next/router";
import LogInPage from "../pages/auth/login";
export default function EnsureAuthenticated(OriginalComponent) {
const router = useRouter();
const loggedInUser = UserManager.getLoggedInUser();
if (loggedInUser ) {
return <OriginalComponent />;
}
return router.push(LogInPage.routePath);
}
And here is my dashboard page that I need to wrap with the above HOC to enforce authentication
import { useEffect } from "react";
import { useRouter } from "next/router";
import EnsureAuthenticated from "../../components/auth_hoc";
function DashboardHomePage(props) {
const router = useRouter();
return (<div>
Home Page
</div>);
}
DashboardHomePage.routePath = "/dashboard/home";
export default EnsureAuthenticated();
Unfortunately for me I keep getting this error after compiling in NextJS
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
Please, how do I resolve this?
Thank you.
You aren't passing DashboardHomepage component to HOC .
HOC code has to changed , we cannot use hooks in HOC body because we are calling HOC as a function instead of a component. When we call a component as a function react excludes all the lifecycles from that.
There is also an issue with the usage of router in your code, as in nextjs if we use it in render it will be rendered and client side and server side.
You may have to create a component which will do routing in useEffect and use it instead of router.push(LogInPage.routePath)
import React from "react";
import UserManager from "../managers/user_manager";
import { useRouter } from "next/router";
import LogInPage from "../pages/auth/login";
export default function EnsureAuthenticated(OriginalComponent) {
return (props) => {
const router = useRouter();
const loggedInUser = UserManager.getLoggedInUser();
if (loggedInUser ) {
return <OriginalComponent />;
}
//below code can give error as router is supposed to be called only at client side, you might have to implement it using useEffect
return router.push(LogInPage.routePath);
};
}
import { useRouter } from "next/router";
import EnsureAuthenticated from "../components/test/AddCount";
function DashboardHomePage(props) {
const router = useRouter();
return <div>Home Page</div>;
}
DashboardHomePage.routePath = "/dashboard/home";
export default EnsureAuthenticated(DashboardHomePage);

react-router throwing TypeError: useContext(...) is undefined when using useHistory

On my own code I tried to use react-router's useHistory by adding it to the imports:
import {BrowserRouter as Router, Link, Route, Switch, useHistory} from "react-router-dom";
and then defining a variable with it on my App() function:
let history = useHistory();
When I do that, I get the error:
TypeError: useContext(...) is undefined
coming from react-router's hooks.js, the specific line is:
return useContext(Context).history;
The whole file looks like this:
import React from "react";
import invariant from "tiny-invariant";
import Context from "./RouterContext.js";
import matchPath from "./matchPath.js";
const useContext = React.useContext;
export function useHistory() {
if (__DEV__) {
invariant(
typeof useContext === "function",
"You must use React >= 16.8 in order to use useHistory()"
);
}
return useContext(Context).history;
}
export function useLocation() {
if (__DEV__) {
invariant(
typeof useContext === "function",
"You must use React >= 16.8 in order to use useLocation()"
);
}
return useContext(Context).location;
}
export function useParams() {
if (__DEV__) {
invariant(
typeof useContext === "function",
"You must use React >= 16.8 in order to use useParams()"
);
}
const match = useContext(Context).match;
return match ? match.params : {};
}
export function useRouteMatch(path) {
if (__DEV__) {
invariant(
typeof useContext === "function",
"You must use React >= 16.8 in order to use useRouteMatch()"
);
}
return path
? matchPath(useLocation().pathname, path)
: useContext(Context).match;
}
Some more context:
I tried accessing React.useContext on my own code and it is defined and it is a function.
Any ideas what might be going on here?
I think that you should wrap your App in index.js with the BrowserRouter (as Router) and then in your App you define the Switch and Routes. Because you cannot use useHistory or useLocation in the same file where you use BrowserRouter.
So, use BrowserRouter wrapper one level up.
That happens because you need to wrap your component in a Router element. e.g:
import React from 'react';
import { Router } from 'react-router-dom';
import { createBrowserHistory } from 'history';
// import MyComponent
const history = createBrowserHistory();
const MyApp = ()=> (
<Router {...{ history }}>
<MyComponent />
</Router>
);
export default MyApp;
It's definately not the case for this specific issue, but maybe someone will end up with a similar issue as I did.
For me the solution was to import hooks from react-router-dom instead of react-router package.
In my case I was trying to implement custom hook in another local package and it turned out that package didn't have react-router nor react-router-dom as dependency.
Error message was exactly the same and no other error from IDE or compiler.
Took me quite a while to figure this out. So the bottom line is: double-check your dependencies.
I've run into a similar issue with useRouteMatch(). I'm not sure if the cause is the same. I receive the error Cannot read property 'match' of undefined from line useContext(Context).match; when calling useRouteMatch() in my tests.
Option 1:
One of the ways the return from useContext can be undefined is if the Context supplied to useContext doesn't include any data. For example if you remove value={{ name: "Pupeno" }} from https://codesandbox.io/s/react-hooks-usecontext-example-wv76d?file=/src/index.js:320-347 you'll see a similar error.
There could be a similar bug in react-router-dom that allows the Context to be empty when it's called from these hooks.
Option 2:
It's hard to tell without looking at your code. It could also be something like https://github.com/ReactTraining/react-router/issues/7332

Cannot read property 'history' of undefined (useHistory hook of React Router 5)

I am using the new useHistory hook of React Router, which came out a few weeks ago. My React-router version is 5.1.2. My React is at version 16.10.1. You can find my code at the bottom.
Yet when I import the new useHistory from react-router, I get this error:
Uncaught TypeError: Cannot read property 'history' of undefined
which is caused by this line in React-router
function useHistory() {
if (process.env.NODE_ENV !== "production") {
!(typeof useContext === "function") ? process.env.NODE_ENV !== "production" ? invariant(false, "You must use React >= 16.8 in order to use useHistory()") : invariant(false) : void 0;
}
return useContext(context).history; <---------------- ERROR IS ON THIS LINE !!!!!!!!!!!!!!!!!
}
Since it is related to useContext and perhaps a conflict with context is at fault, I tried completely removing all calls to useContext, creating the provider, etc. However, that did nothing. Tried with React v16.8; same thing.
I have no idea what could be causing this, as every other feature of React router works fine.
***Note that the same thing happens when calling the other React router hooks, such as useLocation or useParams.
Has anyone else encountered this? Any ideas to what may cause this?
Any help would be greatly appreciated, as I found nothing on the web related to this issue.
import React, {useEffect, useContext} from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import { Switch, useHistory } from 'react-router'
import { useTranslation } from 'react-i18next';
import lazyLoader from 'CommonApp/components/misc/lazyLoader';
import {AppContext} from 'CommonApp/context/context';
export default function App(props) {
const { i18n } = useTranslation();
const { language } = useContext(AppContext);
let history = useHistory();
useEffect(() => {
i18n.changeLanguage(language);
}, []);
return(
<Router>
<Route path="/">
<div className={testClass}>HEADER</div>
</Route>
</Router>
)
}
It's because the react-router context isn't set in that component. Since its the <Router> component that sets the context you could use useHistory in a sub-component, but not in that one.
Here is a very basic strategy for solving this issue:
const AppWrapper = () => {
return (
<Router> // Set context
<App /> // Now App has access to context
</Router>
)
}
const App = () => {
let history = useHistory(); // Works!
...
// Render routes in this component
Then just be sure to use the "wrapper" component instead of App directly.
Note to other people that run into this problem and already have wrapped the component with Router component. Make sure that Router and the useHistory hook are imported from the same package. The same error can be thrown when one of them are imported from react-router and the other one from react-router-dom and the package versions of those packages don't match. Don't use both of them, read about the difference here.
useHistory won't work in the component where you have your Routes because the context which is needed for useHistory is not yet set.
useHistory will work on any child component or components which you have declared in your Router but it won't work on Router's parent component or Router component itself.
The solution is:
in the Main (father) component
import { BrowserRouter } from "react-router-dom";
<BrowserRouter><App /></BrowserRouter>
in the child component (App)
import { withRouter } from "react-router-dom";
function App(props) {
const { i18n } = useTranslation();
const { language } = useContext(AppContext);
let history = useHistory();
useEffect(() => {
i18n.changeLanguage(language);
}, []);
return(
<Route path="/">
<div className={testClass}>HEADER</div>
</Route>
)
}
export default withRouter(App);
I updated my react-router-dom from 5.0.0 to ^5.1.2 and it's been solved. You may notice that the useHistory is in a sub-component.
Use BrowserRouter.
import {
BrowserRouter as Router,
Route,
Switch,
} from 'react-router-dom';
If you use Router, then you need to specify a history for it:
import {
Router,
Route,
Switch,
} from 'react-router-dom';
// Ensure you destructure the createBrowserHistory object
import { createBrowserHistory } from 'history';
const history = createBrowserHistory();
return (
<Router history={history} >
...
</Router>
);
In a short, you should move const history = useHistory(); to your sub-component

this.props.history.push not working in react

I am trying to navigate using history push inside App.jsx.
I have used below code for the same.
componentDidMount() {
if(!this.authService.isAuthenticated())
{
this.props.history.push('/Login');
}
}
But it is giving an error as history is undefined.
I tried logging props object and I got the below output.
Please help me in navigating inside app.jsx.
Export your component with withRouter.
import withRouter.
import { withRouter } from 'react-router'
Export in your component like this.
export default withRouter(MyComponent)//your component name

react-apollo getClient is undefined

There appears to be an error with how I'm using react-apollo. Following the docs, I am attempting to make a basic query with Apollo. This is the error I get in the browser when on the Review page. It appears that this.getClient is undefined and unable to call watchQuery.
react-apollo.browser.umd.js:417 Uncaught TypeError: this.getClient(...).watchQuery is not a function
at GraphQL.createQuery (react-apollo.browser.umd.js:417)
at GraphQL.setInitialProps (react-apollo.browser.umd.js:404)
at GraphQL.componentWillMount (react-apollo.browser.umd.js:260)
etc...
Here is the code for the Review page. I create a Review React Component, declare a graphql-tag that calls the userInfo query, and export the graphql tag connected to the Review page below.
import React from 'react'
import { graphql } from 'react-apollo';
import gql from 'graphql-tag'
class Review extends React.Component {...}
const userInfoQuery = gql`
query userInfoQuery{
userInfo {
_id
email
name {
first
last
}
isVerified
}
}`
const ReviewWithData = graphql(userInfoQuery)(Review)
export default ReviewWithData;
The issue was I was defining the wrong client in the root index.js file. So for the code below, the client I was exporting was not actually the correct Apollo client. So the variable client on the sixth import was importing some other functions. Lesson learned! Should have been more careful!
import React from 'react';
import { render } from 'react-dom';
import { BrowserRouter } from 'react-router-dom'
import { ApolloProvider } from 'react-apollo';
import App from './containers/app';
import client from './services/Apollo'
const CustomerFrontendApp = (
<ApolloProvider client={BitsyApollo.client}>
<BrowserRouter>
<App />
</BrowserRouter>
</ApolloProvider>
);
render(BitsyCustomerFrontendApp, document.getElementById('root'));

Resources