Tout d'abord, je connais assez bien le withRouter HoC, cependant, dans ce cas, cela n'aide pas car je ne veux pas accéder à l'objet history
dans un composant.
J'essaie de réaliser un mécanisme qui redirigera l'utilisateur vers la page de connexion si je reçois en retour un 401 d'un point de terminaison API. Pour faire des requêtes http, j'utilise axios . J'ai environ 60 points de terminaison que je dois couvrir, qui sont utilisés dans une douzaine de composants dans toute mon application.
Je veux créer une fonction décoratrice pour l'objet instance axios, qui:
1. makes the request
2. if fail && error_code = 401, update user route to `/login`
3. if success, return promise
Le problème que j'ai avec ce qui précède est de mettre à jour l'itinéraire de l'utilisateur. Auparavant, dans react-router-v3
, J'aurais pu importer l'objet browserHistory
directement à partir du package react-router, ce qui n'est plus possible.
Donc, ma question est, comment puis-je accéder à l'objet historique en dehors du composant React sans le faire passer par la pile des appels?
react-router v4 fournit également un moyen de partager l'historique via le package history
, à savoir la fonction createBrowserHistory()
.
L'important est de s'assurer que le même objet historique est partagé dans votre application. Pour ce faire, vous pouvez profiter du fait que les modules de nœuds sont des singletons.
Créez un fichier appelé history.js
dans votre projet, avec le contenu suivant:
import { createBrowserHistory } from 'history';
const history = createBrowserHistory();
export default history;
Vous pouvez ensuite simplement l'importer dans votre application via:
import history from "./history.js";
Veuillez noter que seul Router
accepte le prop history
(BrowserRouter
ne le fait pas), alors assurez-vous de mettre à jour votre routeur JSX en conséquence:
import { Router } from "react-router-dom";
import history from "./history.js";
// and then in your JSX:
return (
<Router history={history}>
{/* routes as usuall */}
</Router>
)
Un exemple de travail peut être trouvé sur https://codesandbox.io/s/owQ8Wrk
Je viens de rencontrer ce même problème et voici la solution que j'ai utilisée pour résoudre ce problème.
J'ai fini par créer une fonction d'usine qui renvoie un objet qui a toutes mes fonctions de services. Pour appeler cette fonction d'usine, un objet de la forme suivante doit être fourni.
interface History {
Push: (location: string) => void;
}
Voici une version distillée de ma fonction d'usine.
const services = {};
function servicesFactory(history: History) {
const countries = countriesFactory(history);
const local = {
...countries,
};
Object.keys(local).forEach(key => {
services[key] = local[key];
});
}
Maintenant, le fichier où cette fonction est définie exporte 2 choses.
1) Cette fonction d'usine
2) l'objet services.
Voici à quoi ressemble le service des pays.
function countriesFactory(h: History): CountriesService {
const countries: CountriesService = {
getCountries() {
return request<Countries>({
method: "get",
endpoint: "/api/countries",
}, h)
}
}
return countries;
}
Et enfin voici à quoi ressemble ma fonction request
.
function request<T>({ method, endpoint, body }: Request, history: History): Promise<Response<T>> {
const headers = {
"token": localStorage.getItem("someToken"),
};
const result: Response<T> = {
data: null,
error: null,
};
return axios({
url: endpoint,
method,
data: body,
headers,
}).then(res => {
result.data = res.data;
return result;
}).catch(e => {
if (e.response.status === 401) {
localStorage.clear();
history.Push("/login");
return result;
} else {
result.error = e.response.data;
return result;
}
});
}
Comme vous pouvez le voir, la fonction request
demande à ce que l'objet historique lui soit transmis, ce qu'il obtiendra du service, et le service l'obtiendra de la fabrique de services.
Maintenant, la partie intéressante est que je n'ai plus qu'à appeler cette fonction d'usine et à lui passer l'objet historique une fois dans l'application entière. Après cela, je peux simplement importer l'objet services
et utiliser n'importe quelle méthode dessus sans avoir à me soucier de lui passer l'objet historique.
Voici le code de l'endroit où j'appelle la fonction d'usine de services.
const App = (props: RouteComponentProps) => {
servicesFactory(props.history);
return (
// my app and routes
);
}
J'espère que quelqu'un d'autre qui trouvera cette question trouvera cela utile.