【问题标题】:react-router-dom: Invalid hook call, Hooks can only be called inside of the body of a function componentreact-router-dom: 无效的钩子调用,钩子只能在函数组件的主体内部调用
【发布时间】:2020-02-14 12:28:05
【问题描述】:

我尝试嵌套路由:我在 Catalog 组件中有一个产品目录,它与 url "backoffice/catalog" 匹配。

如果 url 与 "backoffice/catalog/edit" 匹配,我想路由到 Edition 组件,但我需要 Edition 组件成为Catalog的子节点来分享道具。

我真的不明白为什么嵌套路由不起作用,请救救我!如果我的 App 有什么问题,请随时告诉我,我很了解 JavaScript,但我从 React 开始。

这是我的 App 组件:

import React from "react";
import { Route, Switch } from "react-router-dom";
import { Home } from "./components/Static/Home.js";
import { Dashboard } from "./components/Backoffice/Dashboard.js";
import { Catalog } from "./components/Backoffice/catalog/Catalog.js";
import { Login } from "./components/Login/Login.js";
import { Signup } from "./components/Signup/Signup.js";
import { PrivateRoute } from "./components/PrivateRoute.js";
import "./scss/App.scss";
import {Header} from "./components/Structure/Header";
import {BOHeader} from "./components/Structure/Backoffice/Header";
import {List} from "./components/Listing/List";

function App()
{
  return (
    <div className="App">
      <div className="App-content">
          <Switch>
              <Route path='/backoffice' component={BOHeader} />
              <Route path='/' component={Header} />
          </Switch>
          <Switch>
              <Route exact path='/' component={Home} />
              <Route exact path='/login' component={Login} />
              <Route exact path='/signup' component={Signup} />
              <Route path='/listing' component={List}/>
              <PrivateRoute exact path='/backoffice' component={Dashboard}/>
              <PrivateRoute exact path='/backoffice/catalog' component={Catalog}/>
          </Switch>
      </div>
    </div>
  );
}

export default App;

这是我的 Catalog 组件(路由是在 render 方法中制作的:

import React from 'react';
import Data from '../../../Utils/Data';
import {Product} from './Product';
import {Edition} from './Edition';
import {
    BrowserRouter as Router,
    Switch,
    Route,
    Link,
    useRouteMatch,
    useParams
} from "react-router-dom";

export class Catalog extends React.Component
{
    state = {
        title: '',
        products: [],
        editionProduct: null
    };

    obtainProducts = () =>
    {
        Data.products.obtain()
            .then(products => {this.setState({products: products});})
    };

    editProductHandler = product =>
    {
        this.setState({editionProduct: product});
    };

    saveProductHandler = product =>
    {
        Data.products.save(product).then(() => {
            this.state.products.map(item => {
                item = item._id === product._id ? product : item;
                return item;
            })
        });
    };

    deleteProductHandler = event =>
    {
        const productId = event.target.closest('.product-actions').dataset.productid;
        let products = this.state.products.filter(product => {
            return product._id !== productId;
        });
        this.setState({products: products}, () => {
            Data.products.remove(productId);
        });
    };

    displayProducts = () =>
    {
        return this.state.products.map(product => {
           return (
                <li key={product._id} className='catalog-item'>
                   <Product
                       deleteProductHandler={this.deleteProductHandler}
                       editProductHandler={this.editProductHandler}
                       data={product}
                   />
               </li>
            )
        });
    };


    componentWillMount()
    {
        this.obtainProducts();
    }

    render() {
        const Products = this.displayProducts();
        let { path, url } = useRouteMatch();
        return (
            <div className={this.state.editionProduct ? 'catalog edit' : 'catalog'}>
                <h1>Catalog</h1>
                <Switch>
                    <Route exact path={path}>
                        <ul className='catalog-list'>{Products}</ul>
                    </Route>
                    <Route path={`${path}/edit`}>
                        <Edition saveProductHandler={this.saveProductHandler} product={this.state.editionProduct} />
                    </Route>
                </Switch>
            </div>
        );
    }
}

有什么想法吗?

【问题讨论】:

    标签: javascript reactjs routes react-router react-router-dom


    【解决方案1】:

    你不能在Catalog 组件中使用钩子,因为它是一个类组件。所以你有两种方法可以解决你的问题:

    1. 将组件从类重写为函数式。
    2. 不要在Catalog 组件中使用useRouteMatch。如果需要在组件内部获取match数据,则需要使用withRouter高阶组件。

    因此,如果您选择第二种方式,则需要将 Catalog 组件包装在 withRouter 中:

    export default withRouter(Catalog);
    

    将render函数中的一行从:

    let { path, url } = useRouteMatch();
    

    收件人:

    const { path, url } = this.props.match;
    

    不要忘记更改 Catalog 组件的导入,因为现在您的组件导出为默认值。

    【讨论】:

      【解决方案2】:

      由于我在使用 Typescript 设置 React Router 时遇到了同样的问题,我将分 4 个步骤详细说明 Andrii 的答案:

      1 - npm/yarn 包

      yarn add react-router-dom --save
      yarn add @types/react-router-dom --save-dev
      

      或

      npm install react-router-dom --save
      npm install @types/react-router-dom --save-dev
      

      2 - index.tsx


      1) 在导入高阶组件(本例中为 App)时,请勿使用大括号,因为 App 将默认导出;

      2) BrowserRouter 需要在上层而不是将导出为“default withRouter(Class)”的类,以防止出现以下错误:

      “你不应该在路由器之外使用 Route 或 withRouter()”

      import React from 'react';
      import ReactDOM from 'react-dom';
      import { BrowserRouter } from 'react-router-dom';
      import * as serviceWorker from './serviceWorker';
      import App from './app';
      
      ReactDOM.render(
        <BrowserRouter>
          <App />
        </BrowserRouter>,
        document.getElementById('root')
      );
      
      serviceWorker.unregister();
      

      3 - app.tsx


      1) 从 react-router-dom 导入,带有Router & RouteComponentProps(或您自己的 PropType 定义);

      2) 扩展 React.Component 并使用 RouteComponentProps 接口;

      3) 将 props 传递给要共享路由数据的组件;

      4) 将高阶类导出为default withRouter。

      import React, { ReactElement } from 'react';
      import { Switch, Route, withRouter, RouteComponentProps } from 'react-router-dom';
      import { ExpensesPage } from './pages/expenses/expenses.page';
      import { HomePage } from './pages/home/home.page';
      import { HeaderComponent } from './components/header/header.component';
      import './app.scss';
      
      class App extends React.Component<RouteComponentProps> {
        public render(): ReactElement {
          return (
            <div className='playground'>
              <HeaderComponent {...this.props} />
              <div className="playground-content">
                <Switch>
                  <Route exact path='/' component={HomePage} {...this.props} />
                  <Route exact path='/expenses' component={ExpensesPage} {...this.props} />
                </Switch>
              </div>
            </div>
          );
        }
      }
      
      export default withRouter(App);
      

      4 - header.component


      通过您的 RouteComponentProps 扩展您的类,您可以正常访问路由道具作为历史、位置和匹配,如下所示:

      import React, { ReactElement } from 'react';
      import { RouteComponentProps } from 'react-router-dom';
      import './header.component.scss';
      
      export class HeaderComponent extends React.Component<RouteComponentProps> {
        public render(): ReactElement {
          const { location } = this.props;
          console.log(location.pathname);
      
          return (
            <header className="header">
              {/* ... */}
            </header >
          );
        }
      }
      

      希望它有所帮助,因为我在使用 webpack 且没有 redux 的简单环境中使其工作时遇到了一些挑战。上次使用以下版本正常工作:

      {
          "react": "^16.12.0",
          "react-dom": "^16.12.0",
          "react-router-dom": "^5.1.2",
          "sass-loader": "^8.0.2",
          "style-loader": "^1.1.3",
          "typescript": "^3.8.2",
          "webpack": "^4.41.6",
          "webpack-dev-server": "^3.10.3",
      },
      {
          "@types/react-router-dom": "^5.1.3",
          "webpack-cli": "^3.3.11"
      }
      
      

      【讨论】:

        猜你喜欢
        • 2019-11-21
        • 2020-10-27
        • 2021-10-31
        • 1970-01-01
        • 2021-03-23
        • 2020-11-20
        • 2021-05-05
        • 1970-01-01
        • 2021-02-08
        相关资源
        最近更新 更多