Skip to content

Instantly share code, notes, and snippets.

@faceyspacey
Last active May 31, 2017 06:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save faceyspacey/3f281d02bc549e4d75316f2250f48148 to your computer and use it in GitHub Desktop.
Save faceyspacey/3f281d02bc549e4d75316f2250f48148 to your computer and use it in GitHub Desktop.
createLocationReducer with unused reconcileEntries algorithm
// @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