Last active
May 31, 2017 06:38
-
-
Save faceyspacey/3f281d02bc549e4d75316f2250f48148 to your computer and use it in GitHub Desktop.
createLocationReducer with unused reconcileEntries algorithm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @flow | |
import { NOT_FOUND } from '../index' | |
import isServer from '../pure-utils/isServer' | |
import { nestHistory } from '../pure-utils/nestAction' | |
import type { | |
LocationState, | |
RoutesMap, | |
Action, | |
Payload, | |
History | |
} from '../flow-types' | |
export default (initialState: LocationState, routesMap: RoutesMap) => ( | |
state: LocationState = initialState, | |
action: Action | |
): LocationState => { | |
if ( | |
action.type === NOT_FOUND || | |
(routesMap[action.type] && | |
(action.meta.location.current.pathname !== state.pathname || | |
action.meta.location.kind === 'load')) | |
) { | |
return { | |
pathname: action.meta.location.current.pathname, | |
type: action.type, | |
payload: { ...action.payload }, | |
prev: action.meta.location.prev, | |
kind: action.meta.location.kind, | |
history: action.meta.location.history, | |
hasSSR: state.hasSSR, | |
routesMap | |
} | |
} | |
if (action.type === 'Navigation/RESET') { | |
return { | |
...state, | |
history: reconcileEntries(action) | |
} | |
} | |
return state | |
} | |
export const getInitialState = ( | |
currentPathname: string, | |
type: string, | |
payload: Payload, | |
routesMap: RoutesMap, | |
history: History | |
): LocationState => ({ | |
pathname: currentPathname, | |
type, | |
payload, | |
prev: { | |
pathname: '', | |
type: '', | |
payload: {} | |
}, | |
kind: undefined, | |
history: nestHistory(history), | |
hasSSR: isServer() ? true : undefined, // client uses initial server `hasSSR` state setup here | |
routesMap | |
}) | |
type ResetAction = { | |
history: History, | |
basePath: string, | |
resetEntries: Array<{ pathname: string }>, | |
position?: string | |
} | |
export const reconcileEntries = ({ | |
history, | |
basePath, | |
resetEntries, | |
position | |
}: ResetAction) => { | |
const newEntriesCount = resetEntries.length | |
// remove entries that match stack that is reset, | |
// but not the entries in between those from other stacks | |
if (position === 'cherrypick') { | |
let replaceAtIndex | |
let numRemoved = 0 | |
let indexDifference = 0 | |
const entries = history.entries.filter((entry, i) => { | |
const isMatched = entry.pathname.indexOf(basePath) === 0 // should be changed to compare navKeys (and have entry.navKey stored) | |
if (isMatched) { | |
replaceAtIndex = i - numRemoved | |
numRemoved += 1 | |
if (i <= history.index) { | |
indexDifference -= 1 | |
} | |
} | |
return !isMatched | |
}) | |
if (typeof replaceAtIndex !== 'undefined') { | |
entries.splice(replaceAtIndex, 0, ...resetEntries) | |
history.entries = entries | |
const lengthDifference = newEntriesCount - numRemoved | |
history.length += lengthDifference | |
let newIndex = history.index + indexDifference | |
if (replaceAtIndex <= newIndex + 1) { | |
newIndex += newEntriesCount | |
} | |
history.index = newIndex | |
} | |
} | |
else { | |
let startIndex | |
let endIndex | |
let numRemoved = 0 | |
const entries = history.entries.filter((entry, i) => { | |
const isMatched = entry.pathname.indexOf(basePath) === 0 // should be changed to compare navKeys (and have entry.navKey stored) | |
if (isMatched) { | |
// find all entries between the first and last entry corresponding to | |
// the route stack with the given basePath. Then remove them below. | |
if (typeof startIndex === 'undefined') { | |
startIndex = i | |
} | |
else { | |
endIndex = i | |
} | |
numRemoved += 1 | |
} | |
return !isMatched // return the non-removed entries | |
}) | |
// splice together history entries when the user does not want to push the | |
// new routes on to the end of the history stack. There also has to be at | |
// least one item removed (i.e. startIndex is not undefined), or otherwise | |
// there is nothing to replace, in which case, the reset is pushed to the | |
// start or end of the history stack as usual (see last 2 elseifs). | |
if (typeof position === 'undefined' && typeof startIndex !== 'undefined') { | |
if (typeof endIndex === 'undefined') { | |
numRemoved = 1 | |
endIndex = startIndex | |
} | |
else { | |
numRemoved = endIndex - (startIndex + 1) | |
} | |
entries.splice(startIndex, numRemoved, ...resetEntries) | |
history.entries = entries | |
const lengthDifference = newEntriesCount - numRemoved | |
history.length += lengthDifference | |
if (startIndex <= history.index && history.index <= endIndex) { | |
history.index = startIndex + (newEntriesCount - 1) | |
} | |
else if (history.index > endIndex) { | |
history.index += lengthDifference | |
} | |
} | |
// push the new routes/entries on to the start of the history stack | |
// minus possible removed routes from the previous StackRouter state | |
else if (position === 'start') { | |
history.entries = resetEntries.concat(entries) | |
history.length = history.entries.length | |
history.index = history.length - 1 | |
} | |
// push the new routes/entries on to the end of the history stack | |
// minus possible removed routes from the previous StackRouter state. | |
// By default, if neither a position was supplied nor there were any | |
// routes removed, the route stack is placed at the start of the history | |
else if (position === 'end' || typeof startIndex === 'undefined') { | |
history.entries = entries.concat(resetEntries) | |
history.length = history.entries.length | |
history.index = history.length - 1 | |
} | |
} | |
return { | |
index: history.index, | |
length: history.length, | |
entries: history.entries | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment