mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-02-10 17:23:42 +08:00
refactor: create initial state only once
This commit is contained in:
@@ -80,8 +80,8 @@ const StackRouter: Router<CommonAction | Action> = {
|
||||
if (state.routeNames === undefined || state.key === undefined) {
|
||||
state = {
|
||||
...state,
|
||||
routeNames: state.routeNames || routeNames,
|
||||
key: state.key || `stack-${shortid()}`,
|
||||
routeNames,
|
||||
key: `stack-${shortid()}`,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -64,8 +64,8 @@ const TabRouter: Router<Action | CommonAction> = {
|
||||
if (state.routeNames === undefined || state.key === undefined) {
|
||||
state = {
|
||||
...state,
|
||||
routeNames: state.routeNames || routeNames,
|
||||
key: state.key || `tab-${shortid()}`,
|
||||
routeNames,
|
||||
key: `tab-${shortid()}`,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -170,6 +170,7 @@ const TabRouter: Router<Action | CommonAction> = {
|
||||
|
||||
export function TabNavigator(props: Props) {
|
||||
const { navigation, descriptors } = useNavigationBuilder(TabRouter, props);
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'row', height: '100%' }}>
|
||||
{navigation.state.routes.map((route, i, self) => (
|
||||
|
||||
@@ -5,9 +5,9 @@ import NavigationContainer from '../NavigationContainer';
|
||||
import useNavigationBuilder from '../useNavigationBuilder';
|
||||
import { Router } from '../types';
|
||||
|
||||
let key = 0;
|
||||
export const MockRouter: Router<{ type: string }> & { key: number } = {
|
||||
key: 0,
|
||||
|
||||
export const MockRouter: Router<{ type: string }> = {
|
||||
getInitialState({
|
||||
routeNames,
|
||||
initialRouteName = routeNames[0],
|
||||
@@ -16,7 +16,7 @@ export const MockRouter: Router<{ type: string }> = {
|
||||
const index = routeNames.indexOf(initialRouteName);
|
||||
|
||||
return {
|
||||
key: String(key++),
|
||||
key: String(MockRouter.key++),
|
||||
index,
|
||||
routeNames,
|
||||
routes: routeNames.map(name => ({
|
||||
@@ -33,8 +33,8 @@ export const MockRouter: Router<{ type: string }> = {
|
||||
if (state.routeNames === undefined || state.key === undefined) {
|
||||
state = {
|
||||
...state,
|
||||
routeNames: state.routeNames || routeNames,
|
||||
key: state.key || String(key++),
|
||||
routeNames,
|
||||
key: String(MockRouter.key++),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ export const MockRouter: Router<{ type: string }> = {
|
||||
actionCreators: {},
|
||||
};
|
||||
|
||||
beforeEach(() => (key = 0));
|
||||
beforeEach(() => (MockRouter.key = 0));
|
||||
|
||||
it('initializes state for a navigator on navigation', () => {
|
||||
const TestNavigator = (props: any) => {
|
||||
@@ -128,7 +128,7 @@ it('initializes state for a navigator on navigation', () => {
|
||||
expect(onStateChange).toBeCalledTimes(1);
|
||||
expect(onStateChange).toBeCalledWith({
|
||||
index: 0,
|
||||
key: '1',
|
||||
key: '0',
|
||||
routeNames: ['foo', 'bar', 'baz'],
|
||||
routes: [
|
||||
{ key: 'foo', name: 'foo', params: { count: 10 } },
|
||||
@@ -179,7 +179,7 @@ it('rehydrates state for a navigator on navigation', () => {
|
||||
|
||||
expect(onStateChange).lastCalledWith({
|
||||
index: 1,
|
||||
key: '0',
|
||||
key: '2',
|
||||
routeNames: ['foo', 'bar'],
|
||||
routes: [{ key: 'foo', name: 'foo' }, { key: 'bar', name: 'bar' }],
|
||||
});
|
||||
@@ -226,7 +226,7 @@ it('initializes state for nested navigator on navigation', () => {
|
||||
expect(onStateChange).toBeCalledTimes(1);
|
||||
expect(onStateChange).toBeCalledWith({
|
||||
index: 2,
|
||||
key: '4',
|
||||
key: '0',
|
||||
routeNames: ['foo', 'bar', 'baz'],
|
||||
routes: [
|
||||
{ key: 'foo', name: 'foo' },
|
||||
@@ -236,7 +236,7 @@ it('initializes state for nested navigator on navigation', () => {
|
||||
name: 'baz',
|
||||
state: {
|
||||
index: 0,
|
||||
key: '3',
|
||||
key: '1',
|
||||
routeNames: ['qux'],
|
||||
routes: [{ key: 'qux', name: 'qux' }],
|
||||
},
|
||||
@@ -345,7 +345,7 @@ it('cleans up state when the navigator unmounts', () => {
|
||||
expect(onStateChange).toBeCalledTimes(1);
|
||||
expect(onStateChange).lastCalledWith({
|
||||
index: 0,
|
||||
key: '1',
|
||||
key: '0',
|
||||
routeNames: ['foo', 'bar'],
|
||||
routes: [{ key: 'foo', name: 'foo' }, { key: 'bar', name: 'bar' }],
|
||||
});
|
||||
@@ -424,7 +424,7 @@ it("lets parent handle the action if child didn't", () => {
|
||||
expect(onStateChange).toBeCalledTimes(1);
|
||||
expect(onStateChange).lastCalledWith({
|
||||
index: 2,
|
||||
key: '4',
|
||||
key: '0',
|
||||
routeNames: ['foo', 'bar', 'baz'],
|
||||
routes: [
|
||||
{ key: 'baz', name: 'baz' },
|
||||
@@ -474,7 +474,7 @@ it('allows arbitrary state updates by dispatching a function', () => {
|
||||
expect(onStateChange).toBeCalledTimes(1);
|
||||
expect(onStateChange).toBeCalledWith({
|
||||
index: 1,
|
||||
key: '1',
|
||||
key: '0',
|
||||
routeNames: ['foo', 'bar'],
|
||||
routes: [{ key: 'bar', name: 'bar' }, { key: 'foo', name: 'foo' }],
|
||||
});
|
||||
@@ -513,7 +513,7 @@ it('updates route params with setParams', () => {
|
||||
expect(onStateChange).toBeCalledTimes(1);
|
||||
expect(onStateChange).lastCalledWith({
|
||||
index: 0,
|
||||
key: '2',
|
||||
key: '0',
|
||||
routeNames: ['foo', 'bar'],
|
||||
routes: [
|
||||
{ key: 'foo', name: 'foo', params: { username: 'alice' } },
|
||||
@@ -526,7 +526,7 @@ it('updates route params with setParams', () => {
|
||||
expect(onStateChange).toBeCalledTimes(2);
|
||||
expect(onStateChange).lastCalledWith({
|
||||
index: 0,
|
||||
key: '2',
|
||||
key: '0',
|
||||
routeNames: ['foo', 'bar'],
|
||||
routes: [
|
||||
{ key: 'foo', name: 'foo', params: { username: 'alice', age: 25 } },
|
||||
|
||||
@@ -6,6 +6,8 @@ import NavigationContainer from '../NavigationContainer';
|
||||
import Screen from '../Screen';
|
||||
import { MockRouter } from './index.test';
|
||||
|
||||
beforeEach(() => (MockRouter.key = 0));
|
||||
|
||||
it("lets children handle the action if parent didn't", () => {
|
||||
const ParentRouter: Router<{ type: string }> = {
|
||||
...MockRouter,
|
||||
@@ -78,9 +80,9 @@ it("lets children handle the action if parent didn't", () => {
|
||||
name: 'baz',
|
||||
state: {
|
||||
index: 0,
|
||||
key: '3',
|
||||
key: '4',
|
||||
routeNames: ['qux', 'lex'],
|
||||
routes: [{ key: 'qux', name: 'qux' },{ key: 'lex', name: 'lex' }],
|
||||
routes: [{ key: 'qux', name: 'qux' }, { key: 'lex', name: 'lex' }],
|
||||
},
|
||||
},
|
||||
{ key: 'bar', name: 'bar' },
|
||||
@@ -110,7 +112,7 @@ it("lets children handle the action if parent didn't", () => {
|
||||
expect(onStateChange).toBeCalledTimes(1);
|
||||
expect(onStateChange).lastCalledWith({
|
||||
index: 0,
|
||||
key: '2',
|
||||
key: '5',
|
||||
routeNames: ['foo', 'bar', 'baz'],
|
||||
routes: [
|
||||
{
|
||||
@@ -118,7 +120,7 @@ it("lets children handle the action if parent didn't", () => {
|
||||
name: 'baz',
|
||||
state: {
|
||||
index: 0,
|
||||
key: '3',
|
||||
key: '4',
|
||||
routeNames: ['qux', 'lex'],
|
||||
routes: [{ key: 'lex', name: 'lex' }, { key: 'qux', name: 'qux' }],
|
||||
},
|
||||
|
||||
@@ -20,32 +20,30 @@ export default function useNavigationBuilder(
|
||||
) {
|
||||
useRegisterNavigator();
|
||||
|
||||
const [screens] = React.useState(() =>
|
||||
React.Children.map(options.children, child => {
|
||||
if (child === null || child === undefined) {
|
||||
return;
|
||||
}
|
||||
const screens = React.Children.map(options.children, child => {
|
||||
if (child === null || child === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (React.isValidElement(child) && child.type === Screen) {
|
||||
return child.props as ScreenProps;
|
||||
}
|
||||
if (React.isValidElement(child) && child.type === Screen) {
|
||||
return child.props as ScreenProps;
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`A navigator can only contain 'Screen' components as its direct children (found '${
|
||||
// @ts-ignore
|
||||
child.type && child.type.name ? child.type.name : String(child)
|
||||
}')`
|
||||
);
|
||||
})
|
||||
.filter(Boolean)
|
||||
.reduce(
|
||||
(acc, curr) => {
|
||||
acc[curr!.name] = curr as ScreenProps;
|
||||
return acc;
|
||||
},
|
||||
{} as { [key: string]: ScreenProps }
|
||||
)
|
||||
);
|
||||
throw new Error(
|
||||
`A navigator can only contain 'Screen' components as its direct children (found '${
|
||||
// @ts-ignore
|
||||
child.type && child.type.name ? child.type.name : String(child)
|
||||
}')`
|
||||
);
|
||||
})
|
||||
.filter(Boolean)
|
||||
.reduce(
|
||||
(acc, curr) => {
|
||||
acc[curr!.name] = curr as ScreenProps;
|
||||
return acc;
|
||||
},
|
||||
{} as { [key: string]: ScreenProps }
|
||||
);
|
||||
|
||||
const routeNames = Object.keys(screens);
|
||||
const initialRouteName =
|
||||
@@ -60,17 +58,26 @@ export default function useNavigationBuilder(
|
||||
{} as { [key: string]: object | undefined }
|
||||
);
|
||||
|
||||
const {
|
||||
state: currentState = router.getInitialState({
|
||||
const [initialState] = React.useState(() =>
|
||||
router.getInitialState({
|
||||
routeNames,
|
||||
initialRouteName,
|
||||
initialParamsList,
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
const {
|
||||
state: currentState = initialState,
|
||||
getState: getCurrentState,
|
||||
setState,
|
||||
key,
|
||||
} = React.useContext(NavigationStateContext);
|
||||
|
||||
let state = router.getRehydratedState({
|
||||
routeNames,
|
||||
partialState: currentState,
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
return () => {
|
||||
// We need to clean up state for this navigator on unmount
|
||||
@@ -83,13 +90,7 @@ export default function useNavigationBuilder(
|
||||
(): NavigationState =>
|
||||
router.getRehydratedState({
|
||||
routeNames,
|
||||
partialState:
|
||||
getCurrentState() ||
|
||||
router.getInitialState({
|
||||
routeNames,
|
||||
initialRouteName,
|
||||
initialParamsList,
|
||||
}),
|
||||
partialState: getCurrentState() || state,
|
||||
}),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[getCurrentState, router.getRehydratedState, router.getInitialState]
|
||||
@@ -125,16 +126,13 @@ export default function useNavigationBuilder(
|
||||
actionCreators: router.actionCreators,
|
||||
});
|
||||
|
||||
const navigation = React.useMemo(
|
||||
() => ({
|
||||
...helpers,
|
||||
state: currentState,
|
||||
}),
|
||||
[helpers, currentState]
|
||||
);
|
||||
const navigation = React.useMemo(() => ({ ...helpers, state }), [
|
||||
helpers,
|
||||
state,
|
||||
]);
|
||||
|
||||
const descriptors = useDescriptors({
|
||||
state: currentState,
|
||||
state,
|
||||
screens,
|
||||
helpers,
|
||||
onAction,
|
||||
|
||||
Reference in New Issue
Block a user