@lagunovsky/redux-react-router
Version:
A Redux binding for React Router v6
198 lines (190 loc) • 7.67 kB
JavaScript
import { jsx } from 'react/jsx-runtime';
import { useRef, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Router } from 'react-router';
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
function __rest(s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
/**
* This action type will be dispatched when your history
* receives a location change.
*/
const ROUTER_ON_LOCATION_CHANGED = '@@router/ON_LOCATION_CHANGED';
const onLocationChanged = (location, action) => ({
type: ROUTER_ON_LOCATION_CHANGED,
payload: { location, action },
});
function matchLocationChangeAction(action) {
return action.type === ROUTER_ON_LOCATION_CHANGED;
}
/**
* This action type will be dispatched by the history actions below.
* If you're writing a middleware to watch for navigation events, be sure to
* look for actions of this type.
*/
const ROUTER_CALL_HISTORY_METHOD = '@@router/CALL_HISTORY_METHOD';
function updateLocation(method, asEffect = true) {
return (...args) => ({
type: ROUTER_CALL_HISTORY_METHOD,
payload: { method, args, asEffect },
});
}
function matchUpdateLocationActions(action) {
return action.type === ROUTER_CALL_HISTORY_METHOD;
}
/**
* Pushes a new location onto the history stack, increasing its length by one.
* If there were any entries in the stack after the current one, they are
* lost.
*
* @param to - The new URL
* @param state - Data to associate with the new location
*/
const push = updateLocation('push');
const pushStraight = updateLocation('push', false);
/**
* Replaces the current location in the history stack with a new one. The
* location that was replaced will no longer be available.
*
* @param to - The new URL
* @param state - Data to associate with the new location
*/
const replace = updateLocation('replace');
const replaceStraight = updateLocation('replace', false);
/**
* Navigates to the next entry in the stack. Identical to go(1).
*/
const go = updateLocation('go');
const goStraight = updateLocation('go', false);
/**
* Goes back one entry in the history stack. Identical to go(-1).
*/
const back = updateLocation('back');
const backStraight = updateLocation('back', false);
/**
* Navigates to the next entry in the stack. Identical to go(1).
*/
const forward = updateLocation('forward');
const forwardStraight = updateLocation('forward', false);
const routerActions = {
push,
replace,
go,
back,
forward,
};
// Middleware
function createRouterMiddleware(history) {
return () => next => (action) => {
if (!matchUpdateLocationActions(action)) {
return next(action);
}
const updateLocationAction = action;
const { method, args } = updateLocationAction.payload;
const callHistoryMethod = () => {
// Typescript is not able to narrow the arguments types correctly, so we need to handle
// each argument constellation separately
switch (method) {
case 'back':
history.back();
break;
case 'forward':
history.forward();
break;
case 'go':
history[method](...args);
break;
case 'push':
history[method](...args);
break;
case 'replace':
history[method](...args);
break;
}
};
if (updateLocationAction.payload.asEffect === true) {
queueMicrotask(callHistoryMethod);
return;
}
callHistoryMethod();
};
}
// Reducer
const ROUTER_REDUCER_MAP_KEY = 'router';
function createRouterReducer(history) {
const initialRouterState = {
location: history.location,
action: history.action,
};
/*
* This reducer will update the state with the most recent location history
* has transitioned to.
*/
return (state = initialRouterState, action) => {
return matchLocationChangeAction(action) === true ? action.payload : state;
};
}
function createRouterReducerMapObject(history) {
return {
[ROUTER_REDUCER_MAP_KEY]: createRouterReducer(history),
};
}
function reduxRouterSelector(state) {
return state[ROUTER_REDUCER_MAP_KEY];
}
function ReduxRouter(_a) {
var { routerSelector = reduxRouterSelector } = _a, props = __rest(_a, ["routerSelector"]);
const dispatch = useDispatch();
const skipHistoryChange = useRef(undefined);
const state = useSelector(routerSelector);
useEffect(() => {
return props.history.listen((nextState) => {
if (skipHistoryChange.current === true) {
skipHistoryChange.current = false;
return;
}
dispatch(onLocationChanged(nextState.location, nextState.action));
});
}, [props.history, dispatch]);
useEffect(() => {
if (props.history.location !== state.location) {
dispatch(onLocationChanged(props.history.location, props.history.action));
}
}, []);
useEffect(() => {
if (skipHistoryChange.current === undefined) {
skipHistoryChange.current = false;
}
else if (props.history.location !== state.location) {
skipHistoryChange.current = true;
props.history.replace(state.location);
}
}, [state.location, props.history]);
return (jsx(Router, { navigationType: state.action, location: state.location, basename: props.basename, navigator: props.history, children: props.children }));
}
export { ROUTER_CALL_HISTORY_METHOD, ROUTER_ON_LOCATION_CHANGED, ROUTER_REDUCER_MAP_KEY, ReduxRouter, back, backStraight, createRouterMiddleware, createRouterReducer, createRouterReducerMapObject, forward, forwardStraight, go, goStraight, matchLocationChangeAction, matchUpdateLocationActions, onLocationChanged, push, pushStraight, reduxRouterSelector, replace, replaceStraight, routerActions };
//# sourceMappingURL=index.es.js.map