繁体   English   中英

react-router-dom: 无效的钩子调用,只能在 function 组件的主体内部调用钩子

[英]react-router-dom: Invalid hook call, Hooks can only be called inside of the body of a function component

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

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

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

这是我的应用程序组件:

 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> ); } }

有任何想法吗?

您不能在Catalog组件中使用挂钩,因为它是 class 组件。 因此,您有两种方法可以解决您的问题:

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

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

export default withRouter(Catalog);

render function 中的一行从:

let { path, url } = useRouteMatch();

至:

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

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

由于我在使用 Typescript 设置我的 React 路由器时遇到了同样的问题,我将分 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 - 索引.tsx


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

2) BrowserRouter 需要在上层而不是 class 将被导出为“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 - 应用程序.tsx


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

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

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

4) 使用Router默认导出高阶 class

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 扩展您的 class,您可以正常访问路由道具作为历史、位置和匹配,如下所示:

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"
}

导出时尝试添加useParams

export default (props) => <NewComponent {...props} {...useParams()} />

完整示例:

<Route exact path='/event/:id' element={<NewComponent />}/>

inport {useParams } from "react-router-dom";

class NewComponent extends React.Component {
  render() {
    return <div>{this.props.id}</div>
  }
}

// Bind url parameters here
export default (props) => <NewComponent {...useParams()} {...props} />

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM