Categories
React Answers

How to wait for an async action before route transition with React Router?

Sometimes, we want to wait for an async action before route transition with React Router.

In this article, we’ll look at how to wait for an async action before route transition with React Router.

How to wait for an async action before route transition with React Router?

To wait for an async action before route transition with React Router, we can create our own hook that calls history.push when our action is done.

For instance, we write

export const useBlock = (func) => {
  const { block, push, location } = useHistory();
  const lastLocation = useRef();

  const funcRef = useRef();
  funcRef.current = func;

  useEffect(() => {
    if (location === lastLocation.current || !funcRef.current) {
      return;
    }

    lastLocation.current = location;

    const unblock = block((location, action) => {
      const doBlock = async () => {
        if (!(await funcRef.current(location, action))) {
          unblock();
          push(location);
        }
      };
      doBlock();
      return false;
    });
  }, [location, block, push]);
};

to create the useBlock hook that calls useEffect with a function that calls block if lastLocation.current isn’t the same as location.

In the block callback, we check if the funcRef.current function returns a value

If it’s doesn’t return a value, we call unblock and push to do the navigation.

Then we use it by writing

const Comp = () => {
  useBlock(async (location) => await fetchShouldBlock(location));

  return <span>Hello</span>;
};

to call useBlock with the async function we want to run before navigation.

Conclusion

To wait for an async action before route transition with React Router, we can create our own hook that calls history.push when our action is done.

Categories
React Answers

How to mock the useHistory hook in Jest?

Sometimes, we want to mock the useHistory hook in Jest.

In this article, we’ll look at how to mock the useHistory hook in Jest.

How to mock the useHistory hook in Jest?

To mock the useHistory hook in Jest, we can call jest.mock with a function to return the return value of useHistory.

For instance, we write

jest.mock("react-router-dom", () => ({
  useHistory: () => ({
    push: jest.fn(),
  }),
}));

to call jest.mock to mock the react-router-dom module and a callback.

In the callback, we return an object that has the useHistory property set to a function that returns the object we want.

We mock the push method by setting it to jest.fn.

Conclusion

To mock the useHistory hook in Jest, we can call jest.mock with a function to return the return value of useHistory.

Categories
React Answers

How to add nested routes with React Router v5?

Sometimes, we want to add nested routes with React Router v5.

In this article, we’ll look at how to add nested routes with React Router v5.

How to add nested routes with React Router v5?

To add nested routes with React Router v5, we can add Route components in a Switch component.

For instance, we write

const PrivateRoutes = () => {
  return (
    <>
      <Header />
      <Switch>
        <Route path="/home" component={HomePage} />
        <Route path="/dashboard" component={DashboardPage} />
        <Route path="*" component={NotFoundPage} />
      </Switch>
      <Footer />
    </>
  );
};

const AuthRoutes = () => {
  return (
    <>
      <Header />
      <Switch>
        <Route path="/login" component={LoginPage} />
        <Route path="/sign-up" component={SignUpPage} />
        <Route path="*" component={NotFoundPage} />
      </Switch>
      <Footer />
    </>
  );
};

const App = () => {
  const history = createBrowserHistory();

  return (
    <div className="App">
      <Router history={history}>
        {isLoggedIn ? <PrivateRoutes /> : <AuthRoutes />}
      </Router>
    </div>
  );
};

to add the PrivateRoutes and AuthRoues components.

They both have Switch components wrapped around Route components.

In App, we render PrivateRoutes or AuthRoutes depending on the value of isLoggedIn.

Conclusion

To add nested routes with React Router v5, we can add Route components in a Switch component.

Categories
React Answers

How to fix ‘Error: useRoutes() may be used only in the context of a router component’ with React Router v6?

To fix ‘Error: useRoutes() may be used only in the context of a router component’ with React Router v6, we should call useRoutes in the component that’s use to render the route components.

For instance, we write

import React from "react";
import {
  BrowserRouter as Router,
  Routes,
  Route,
  useRoutes,
} from "react-router-dom";

const Component1 = () => {
  return <h1>foo</h1>;
};

const Component2 = () => {
  return <h1>bar</h1>;
};

const App = () => {
  const routes = useRoutes([
    { path: "/", element: <Component1 /> },
    { path: "component2", element: <Component2 /> },
    // ...
  ]);
  return routes;
};

const AppWrapper = () => {
  return (
    <Router>
      <App />
    </Router>
  );
};

export default AppWrapper;

to create the App component that calls the useRoutes hook with an array of route items.

Each item has the path that we go to to render the element.

And we render the routes by returning the routes object returned by useRoutes.

And then we put App inside Router to let us use React Router for navigation.

Categories
React Answers

How to fix the “React-router TypeError: _this.props.history is undefined” error with React Router?

Sometimes, we want to fix the "React-router TypeError: _this.props.history is undefined" error with React Router.

In this article, we’ll look at how to fix the "React-router TypeError: _this.props.history is undefined" error with React Router.

How to fix the "React-router TypeError: _this.props.history is undefined" error with React Router?

To fix the "React-router TypeError: _this.props.history is undefined" error with React Router, we wrap our route components with the BrowserRouter component.

For instance, we write

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import "./index.css";
import { BrowserRouter, Route } from "react-router-dom";

ReactDOM.render(
  <BrowserRouter>
    <Route path="/" component={App} />
  </BrowserRouter>,
  document.getElementById("root")
);

to wrap BrowserRouter around the Router we have to make this.props.history available in App.

We should also call withRouter with App before exporting App to inject other React Router properties as props into App.

Conclusion

To fix the "React-router TypeError: _this.props.history is undefined" error with React Router, we wrap our route components with the BrowserRouter component.