mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-28 12:15:37 +08:00
[Navigator]: Add a method keyOf to NavigationRouteStack.
Summary:
# Summary
Add a method `keyOf` to NavigationRouteStack.
The method `keyOf` returns a key that is associated with the route.
The a route is added to a stack, the stack creats an unique key for it and
will keep the key for the route until the route is rmeoved from the stack.
The stack also passes the keys to its derived stack (the new stack created by the
mutation API such as `push`, `pop`...etc).
The key for the route persists until the initial stack and its derived stack no longer
contains this route.
# Why Do We Need This?
Navigator has needs to use an unique key to manage the scenes rendered.
The problem is that `route` itself isn't a very reliable thing to be used as the key.
Consider this example:
```
// `scene_1` animates into the viewport.
navigator.push('scene_1');
setTimeout(() => {
// `scene_1` animates off the viewport.
navigator.pop();
}, 100);
setTimeout(() => {
// Should we bring in a new scene or bring back the one that was previously popped?
navigator.push('scene_1');
}, 200);
```
Because we currently use `route` itself as a key for the scene, we'd have to block a route
until its scene is completely off the components tree even the route itself is no longer
in the stack otherwise we'd see strange animation of jumping scenes.
# What's Next
We're hoping that we can build pure reactive view for NavigationRouteStack easily.
The naive implementation of NavigationRouteStackView may look like this:
```
class NavigationRouteStackView {
constructor() {
this.state = {
staleScenes: {},
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.stack !== this.props.stack) {
var stale;
var staleScenes = {...this.state.staleScenes};
this.props.stack.forEach((route, index, key) => {
if (nextProps.stack.keyOf(route) !== key) {
stale = true;
staleScenes[key] = {route, index, key, stale};
}
});
if (stale) {
this.setState({
staleScenes,
});
}
}
}
render() {
var scenes = [];
this.props.stack.forEach((route, index, key) => {
scenes.push({route, index, key});
});
Object.keys(this.state.staleScenes).forEach(key => {
scenes.push(this.state.staleScenes[key]);
});
scenes.sort(stableSortByIndex);
return <View>{scenes.map(renderScene)}</View>;
}
}
```
This commit is contained in:
@@ -24,15 +24,23 @@
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
||||
jest
|
||||
.dontMock('NavigationRouteStack')
|
||||
.dontMock('clamp')
|
||||
.dontMock('invariant')
|
||||
.dontMock('immutable');
|
||||
.autoMockOff()
|
||||
.mock('ErrorUtils');
|
||||
|
||||
var NavigationRouteStack = require('NavigationRouteStack');
|
||||
|
||||
describe('NavigationRouteStack:', () => {
|
||||
// Different types of routes.
|
||||
var ROUTES = [
|
||||
'foo',
|
||||
1,
|
||||
true,
|
||||
{foo: 'bar'},
|
||||
['foo'],
|
||||
];
|
||||
|
||||
// Basic
|
||||
it('gets index', () => {
|
||||
var stack = new NavigationRouteStack(1, ['a', 'b', 'c']);
|
||||
@@ -76,6 +84,92 @@ describe('NavigationRouteStack:', () => {
|
||||
expect(stack.indexOf('c')).toBe(-1);
|
||||
});
|
||||
|
||||
// Key
|
||||
it('gets key for route', () => {
|
||||
var test = (route) => {
|
||||
var stack = new NavigationRouteStack(0, ['a']);
|
||||
var key = stack.push(route).keyOf(route);
|
||||
expect(typeof key).toBe('string');
|
||||
expect(!!key).toBe(true);
|
||||
};
|
||||
|
||||
ROUTES.forEach(test);
|
||||
});
|
||||
|
||||
it('gets a key of larger value for route', () => {
|
||||
var lastKey = '';
|
||||
var test = (route) => {
|
||||
var stack = new NavigationRouteStack(0, ['a']);
|
||||
var key = stack.push(route).keyOf(route);
|
||||
expect(key > lastKey).toBe(true);
|
||||
lastKey = key;
|
||||
};
|
||||
|
||||
ROUTES.forEach(test);
|
||||
});
|
||||
|
||||
it('gets an unique key for a different route', () => {
|
||||
var stack = new NavigationRouteStack(0, ['a']);
|
||||
var keys = {};
|
||||
|
||||
var test = (route) => {
|
||||
stack = stack.push(route);
|
||||
var key = stack.keyOf(route);
|
||||
expect(keys[key]).toBe(undefined);
|
||||
keys[key] = true;
|
||||
};
|
||||
|
||||
ROUTES.forEach(test);
|
||||
});
|
||||
|
||||
it('gets the same unique key for the same route', () => {
|
||||
var test = (route) => {
|
||||
var stack = new NavigationRouteStack(0, [route]);
|
||||
expect(stack.keyOf(route)).toBe(stack.keyOf(route));
|
||||
};
|
||||
|
||||
ROUTES.forEach(test);
|
||||
});
|
||||
|
||||
|
||||
it('gets the same unique key form the derived stack', () => {
|
||||
var test = (route) => {
|
||||
var stack = new NavigationRouteStack(0, [route]);
|
||||
var derivedStack = stack.push('wow').pop().slice(0, 10).push('blah');
|
||||
expect(derivedStack.keyOf(route)).toBe(stack.keyOf(route));
|
||||
};
|
||||
|
||||
ROUTES.forEach(test);
|
||||
});
|
||||
|
||||
it('gets a different key from a different stack', () => {
|
||||
var test = (route) => {
|
||||
var stack1 = new NavigationRouteStack(0, [route]);
|
||||
var stack2 = new NavigationRouteStack(0, [route]);
|
||||
expect(stack1.keyOf(route)).not.toBe(stack2.keyOf(route));
|
||||
};
|
||||
|
||||
ROUTES.forEach(test);
|
||||
});
|
||||
|
||||
it('gets no key for a route that does not contains this route', () => {
|
||||
var stack = new NavigationRouteStack(0, ['a']);
|
||||
expect(stack.keyOf('b')).toBe(null);
|
||||
});
|
||||
|
||||
it('gets a new key for a route that was removed and added again', () => {
|
||||
var test = (route) => {
|
||||
var stack = new NavigationRouteStack(0, ['a']);
|
||||
|
||||
var key1 = stack.push(route).keyOf(route);
|
||||
var key2 = stack.push(route).pop().push(route).keyOf(route);
|
||||
expect(key1).not.toBe(key2);
|
||||
};
|
||||
|
||||
ROUTES.forEach(test);
|
||||
});
|
||||
|
||||
// Slice
|
||||
it('slices', () => {
|
||||
var stack1 = new NavigationRouteStack(1, ['a', 'b', 'c', 'd']);
|
||||
var stack2 = stack1.slice(1, 3);
|
||||
@@ -226,4 +320,25 @@ describe('NavigationRouteStack:', () => {
|
||||
stack.replaceAtIndex(100, 'x');
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
// Iteration
|
||||
it('iterates each item', () => {
|
||||
var stack = new NavigationRouteStack(0, ['a', 'b']);
|
||||
var logs = [];
|
||||
var keys = {};
|
||||
|
||||
stack.forEach((route, index, key) => {
|
||||
logs.push([
|
||||
route,
|
||||
index,
|
||||
(typeof key === 'string' && key.length > 0 && !(key in keys)),
|
||||
]);
|
||||
keys[key] = true;
|
||||
});
|
||||
|
||||
expect(logs).toEqual([
|
||||
['a', 0, true],
|
||||
['b', 1, true],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user