admin 管理员组

文章数量: 1086019

I'am using react-router-dom v6 and mobex in my project and get an error when I use it as below.

main.tsx:

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './app/layout/App'
import './app/layout/styles.css'
import 'semantic-ui-css/semantic.min.css'
import 'react-toastify/dist/ReactToastify.min.css'
import 'react-calendar/dist/Calendar.css'
import 'react-datepicker/dist/react-datepicker.css'
import { store, StoreContext } from './app/stores/store'
import { BrowserRouter } from 'react-router-dom'
import { useNavigate } from 'react-router-dom';
export const navigate = useNavigate();

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <StoreContext.Provider value={store}>
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </StoreContext.Provider>
  </React.StrictMode>
)

userStore.ts:

import { UserFormValues } from './../models/user';
import { makeAutoObservable, runInAction } from "mobx";
import { User } from "../models/user";
import agent from '../api/agent';
import { store } from './store';
import { navigate } from '../../main';

export default class UserStore {
    user:User | null = null;

    constructor() {
        makeAutoObservable(this);
    }

    logout = () => {
        storemonStore.setToken(null);
        window.localStorage.removeItem('jwt');
        this.user = null;
        navigate('/');
    }
}

Error:

"Invalid hook call. Hooks can only be called inside the body of a function ponent"

In the old version it was working when I did it like this below:

main.tsx:

...
...
import { createBrowserHistory } from "history";
export const history = createBrowserHistory();
...
...

and userStore.tsx:

import { makeAutoObservable, runInAction } from "mobx";
import agent from "../api/agent";
import { User, UserFormValues } from "../models/user";
import { store } from "./store";
import { history } from "../../index";

export default class UserStore {
  user: User | null = null;

  constructor() {
    makeAutoObservable(this);
  }

  logout = () => {
    storemonStore.setToken(null);
    window.localStorage.removeItem("jwt");
    this.user = null;
    history.push("/");
  };

How can i solve this problem?

I'am using react-router-dom v6 and mobex in my project and get an error when I use it as below.

main.tsx:

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './app/layout/App'
import './app/layout/styles.css'
import 'semantic-ui-css/semantic.min.css'
import 'react-toastify/dist/ReactToastify.min.css'
import 'react-calendar/dist/Calendar.css'
import 'react-datepicker/dist/react-datepicker.css'
import { store, StoreContext } from './app/stores/store'
import { BrowserRouter } from 'react-router-dom'
import { useNavigate } from 'react-router-dom';
export const navigate = useNavigate();

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <StoreContext.Provider value={store}>
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </StoreContext.Provider>
  </React.StrictMode>
)

userStore.ts:

import { UserFormValues } from './../models/user';
import { makeAutoObservable, runInAction } from "mobx";
import { User } from "../models/user";
import agent from '../api/agent';
import { store } from './store';
import { navigate } from '../../main';

export default class UserStore {
    user:User | null = null;

    constructor() {
        makeAutoObservable(this);
    }

    logout = () => {
        store.monStore.setToken(null);
        window.localStorage.removeItem('jwt');
        this.user = null;
        navigate('/');
    }
}

Error:

"Invalid hook call. Hooks can only be called inside the body of a function ponent"

In the old version it was working when I did it like this below:

main.tsx:

...
...
import { createBrowserHistory } from "history";
export const history = createBrowserHistory();
...
...

and userStore.tsx:

import { makeAutoObservable, runInAction } from "mobx";
import agent from "../api/agent";
import { User, UserFormValues } from "../models/user";
import { store } from "./store";
import { history } from "../../index";

export default class UserStore {
  user: User | null = null;

  constructor() {
    makeAutoObservable(this);
  }

  logout = () => {
    store.monStore.setToken(null);
    window.localStorage.removeItem("jwt");
    this.user = null;
    history.push("/");
  };

How can i solve this problem?

Share Improve this question edited Jul 30, 2022 at 10:55 Youssouf Oumar 46.6k16 gold badges103 silver badges105 bronze badges asked Jul 30, 2022 at 10:21 erhanbasturkerhanbasturk 611 silver badge5 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 3

This line const navigate = useNavigate() can only be inside a React ponent or custom hook, as useNavigate is a hook. See Rules of Hooks to know more. One way to solve this is to change your logout function, so it takes navigate as a parameter, like so:

logout = (navigate) => {
  store.monStore.setToken(null);
  window.localStorage.removeItem("jwt");
  this.user = null;
  navigate("/");
};

And call useNavigate in the ponent where you call logout and give navigate to it as a parameter. Like so as an example:

import UserStore from "./UserStore"; // use the correct path
import { useNavigate } from "react-router-dom";

export default function Component() {
  const navigate = useNavigate();
  const userStore = UserStore();
  userStore.logout(navigate);

  return <div></div>;
}

Issue

React hooks can only be used within a React function ponent and the useNavigate hook can only be used within a routing context.

Solution

Access the navigate function via the useNavigate hook from a React function ponent and pass a reference to navigate to the logout handler.

logout = ({ navigate }) => {
  store.monStore.setToken(null);
  window.localStorage.removeItem("jwt");
  this.user = null;
  navigate("/");
};

...

const SomeComponent = () => {
  const navigate = useNavigate();

  ...
  UserStore.logout({ navigate });
  ...
};

Alternative Solution

Import and use the HistoryRouter and continue using the custom history object as previously. This requires history@5 package dependency, install it if necessary (run npm i -S history@5).

Example:

Create and export history object.

import { createBrowserHistory } from "history";
export const history = createBrowserHistory();

...

...
import { unstable_HistoryRouter as HistoryRouter } from 'react-router-dom';
import { history } from '../path/to/history';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <StoreContext.Provider value={store}>
      <HistoryRouter history={history}>
        <App />
      </HistoryRouter>
    </StoreContext.Provider>
  </React.StrictMode>
);

UserStore continues to import the history object and use it.

import { makeAutoObservable, runInAction } from "mobx";
import agent from "../api/agent";
import { User, UserFormValues } from "../models/user";
import { store } from "./store";
import { history } from "../path/to/history";

export default class UserStore {
  user: User | null = null;

  constructor() {
    makeAutoObservable(this);
  }

  logout = () => {
    store.monStore.setToken(null);
    window.localStorage.removeItem("jwt");
    this.user = null;
    history.push("/");
  };

  ...

You cannot use hooks in class ponent. for route trough class ponent there is another way to do that. but I think in router 6 class ponent support is dropped. so you can change your class ponent to function ponent it is easy way.

本文标签: javascriptreactrouterdom v6 history push and useNavigate problemStack Overflow