React-router-v6 accede a un parámetro de URL

Resuelto ziad hany asked hace 3 años • 8 respuestas

¿Cómo puedo acceder al parámetro de URL en mi componente de reacción?

aplicación.js

<Route path="/question/:id" element={<QuestionView />} />

PreguntaView.js

class QuestionView extends React.Component {     
    render() {
          const { questions, users } = this.props;
          const {id} = ??? 
ziad hany avatar Nov 15 '21 05:11 ziad hany
Aceptado

Asunto

En react-router-dom v6, los Routecomponentes ya no tienen accesorios de ruta ( history, locationy match), y la solución actual es usar las "versiones" de los ganchos de React para usar dentro de los componentes que se procesan. Sin embargo, los ganchos de React no se pueden usar en componentes de clase.

Para acceder a los parámetros de coincidencia con un componente de clase, debe convertirlo a un componente de función o ejecutar su propio withRoutercomponente de orden superior personalizado para inyectar los "accesorios de ruta" como lo hizo el withRouterHOC de v5.x.react-router-dom

Solución

No cubriré la conversión de un componente de clase en un componente de función. Aquí hay un ejemplo withRouterde HOC personalizado:

const withRouter = WrappedComponent => props => {
  const params = useParams();
  // etc... other react-router-dom v6 hooks

  return (
    <WrappedComponent
      {...props}
      params={params}
      // etc...
    />
  );
};

Y decora el componente con el nuevo HOC.

export default withRouter(Post);

Esto inyectará un paramsaccesorio para el componente de clase.

this.props.params.id
Drew Reese avatar Nov 14 '2021 23:11 Drew Reese

withRouterVersión HOC TypeScript con parámetros genéricos

conRouter.tsx

import { ComponentType } from 'react';
import { useLocation, useNavigate, useParams } from 'react-router-dom';

export interface WithRouterProps<T = ReturnType<typeof useParams>> {
  history: {
    back: () => void;
    goBack: () => void;
    location: ReturnType<typeof useLocation>;
    push: (url: string, state?: any) => void;
  }
  location: ReturnType<typeof useLocation>;
  match: {
    params: T;
  };
  navigate: ReturnType<typeof useNavigate>;
}

export const withRouter = <P extends object>(Component: ComponentType<P>) => {
  return (props: Omit<P, keyof WithRouterProps>) => {
    const location = useLocation();
    const match = { params: useParams() };
    const navigate = useNavigate();

    const history = {
      back: () => navigate(-1),
      goBack: () => navigate(-1),
      location,
      push: (url: string, state?: any) => navigate(url, { state }),
      replace: (url: string, state?: any) => navigate(url, {
        replace: true,
        state
      })
    };

    return (
      <Component
        history={history}
        location={location}
        match={match}
        navigate={navigate}
        {...props as P}
      />
    );
  };
};

MiClase.tsx

import { Component } from 'react';

import { withRouter, WithRouterProps } from './withRouter';

interface Params {
  id: string;
}

type Props = WithRouterProps<Params>;

class MyClass extends Component<Props> {
  render() {
    const { match } = this.props;
    console.log(match.params.id); // with autocomplete
    return <div>MyClass</div>;
  }
}

export default withRouter(MyClass);
egor.xyz avatar Feb 01 '2022 08:02 egor.xyz

Si desea utilizar una clase, deberá envolverla con el archivo withRouter. Proporciono un ejemplo a continuación:

Esta es mi clase para la forma de película:

class MovieForm extends Form {

    state = {
        data: {
            title: "",
            genreId: "",
            numberInStock: "",
            dailyRentalRate: ""
        },
        genres: [],
        errors: {}
    };

    schema = {
        _id: Joi.string(),
        title: Joi.string()
            .required()
            .label("Title"),
        genreId: Joi.string()
            .required()
            .label("Genre"),
        numberInStock: Joi.number()
            .required()
            .min(0)
            .max(100)
            .label("Number in Stock"),
        dailyRentalRate: Joi.number()
            .required()
            .min(0)
            .max(10)
            .label("Daily Rental Rate")
    };




    componentDidMount() {

        const genres = getGenres();
        this.setState({ genres });

        // const movieId = this.props.match.params.id;
        const movieId = this.props.params.id;
        if (movieId === "new") return;

        const movie = getMovie(movieId);
        if (!movie) return this.props.history.replace("/not-found");

        this.setState({ data: this.mapToViewModel(movie) });
    }

    mapToViewModel(movie) {
        return {
            _id: movie._id,
            title: movie.title,
            genreId: movie.genre._id,
            numberInStock: movie.numberInStock,
            dailyRentalRate: movie.dailyRentalRate
        };
    }


    doSubmit = () => {
        saveMovie(this.state.data);
        this.props.navigate("/movies");
    };



    render() {

        return (
            <div>
                <h1>Movie Form</h1>
                <form onSubmit={this.handleSubmit}>
                    {this.renderInput("title", "Title")}
                    {this.renderSelect("genreId", "Genre", this.state.genres)}
                    {this.renderInput("numberInStock", "Number in Stock", "number")}
                    {this.renderInput("dailyRentalRate", "Rate")}
                    {this.renderButton("Save")}
                </form>
            </div>
        );
    }
}

Escribo un contenedor fuera de la clase:

const withRouter = WrappedComponent => props => {
    const params = useParams();
    const navigate = useNavigate();

    return (
        <WrappedComponent
            {...props}
            params={params}
            navigate={navigate}
        />
    );
};

Ahora, al final del archivo lo exportaré como se muestra a continuación:

export default withRouter(MovieForm);

Dentro de withRouter, obtengo todas las funciones que usaré más adelante dentro de la clase:

const params = useParams();
const navigate = useNavigate();
Arefe avatar Mar 16 '2022 08:03 Arefe