webentwicklung-frage-antwort-db.com.de

React-Router-Dom: Ungültiger Hook-Aufruf. Hooks können nur innerhalb des Körpers einer Funktionskomponente aufgerufen werden

Ich versuche, eine Route zu verschachteln: Ich habe einen Produktkatalog in einer Katalog Komponente, die mit der URL "backoffice/catalog" übereinstimmt.

Ich möchte zur Komponente Edition Weiterleiten, wenn die URL mit "backoffice/catalog/edit" übereinstimmt, aber ich benötige die Edition Komponente, um ein Kind von Katalog zu sein, um Requisiten zu teilen.

Ich verstehe wirklich nicht, warum die verschachtelte Route nicht funktioniert, bitte rette mich! Und zögern Sie nicht, mir zu sagen, wenn etwas mit meiner App nicht stimmt. Ich kenne Javascript gut, aber ich beginne mit React: D.

Hier ist meine App-Komponente:

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;

Hier ist meine Katalogkomponente (die Route wird in der Rendermethode erstellt:

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

Irgendwelche Ideen ?

6
Janus

Da ich beim Einrichten meines Routers React Router mit TypeScript) das gleiche Problem hatte, werde ich in 4 Schritten ein wenig mehr Andrii-Antworten detaillieren:

1 - npm/Garnpakete

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

oder

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

2 - index.tsx


1) Verwenden Sie beim Importieren Ihrer Komponente höherer Ordnung (im vorliegenden Fall App) keine geschweiften Klammern, da die App standardmäßig exportiert wird.

2) BrowserRouter muss sich in einer höheren Ebene befinden und nicht in der Klasse, die als "Standard mit Router (Klasse)" exportiert wird, um den folgenden Fehler zu vermeiden:

"Sie sollten Route oder withRouter () nicht außerhalb eines Routers verwenden."

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) Importieren aus React-Router-Dom, withRouter & RouteComponentProps (oder Ihrer eigenen PropType-Definition);

2) Erweitern Sie React.Component und verwenden Sie die RouteComponentProps-Schnittstelle.

3) Übergeben Sie die Requisiten an Komponenten, für die Sie Routing-Daten freigeben möchten.

4) Exportieren Sie die Klasse höherer Ordnung als Standard mit Router.

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


Über Ihre RouteComponentProps, die Ihre Klasse erweitern, können Sie normalerweise auf die Routing-Requisiten als Verlauf, Standort und Übereinstimmung wie folgt zugreifen:

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

Ich hoffe, es hilft, denn ich hatte eine Herausforderung, dies in einer einfachen Umgebung mit Webpack und ohne Redux zum Laufen zu bringen. Das letzte Mal ordnungsgemäß mit den folgenden Versionen arbeiten:

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

1
Daniel Santana