mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-02-10 17:23:42 +08:00
refactor: move navigation.state to a route prop (#6)
This commit is contained in:
committed by
satyajit.happy
parent
b775dbaacf
commit
d3099c18b8
@@ -262,11 +262,11 @@ const StackRouter: Router<CommonAction | Action> = {
|
||||
};
|
||||
|
||||
export function StackNavigator(props: Props) {
|
||||
const { navigation, descriptors } = useNavigationBuilder(StackRouter, props);
|
||||
const { state, descriptors } = useNavigationBuilder(StackRouter, props);
|
||||
|
||||
return (
|
||||
<div style={{ position: 'relative' }}>
|
||||
{navigation.state.routes.map((route, i) => (
|
||||
{state.routes.map((route, i) => (
|
||||
<div
|
||||
key={route.key}
|
||||
style={{
|
||||
@@ -297,10 +297,7 @@ export function StackNavigator(props: Props) {
|
||||
boxShadow: '0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23)',
|
||||
}}
|
||||
>
|
||||
{
|
||||
descriptors[navigation.state.routes[navigation.state.index].key]
|
||||
.options.title
|
||||
}
|
||||
{descriptors[state.routes[state.index].key].options.title}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -184,18 +184,18 @@ const TabRouter: Router<Action | CommonAction> = {
|
||||
};
|
||||
|
||||
export function TabNavigator(props: Props) {
|
||||
const { navigation, descriptors } = useNavigationBuilder(TabRouter, props);
|
||||
const { state, descriptors } = useNavigationBuilder(TabRouter, props);
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'row', height: '100%' }}>
|
||||
{navigation.state.routes.map((route, i, self) => (
|
||||
{state.routes.map((route, i, self) => (
|
||||
<div
|
||||
key={route.key}
|
||||
style={{
|
||||
width: `${100 / self.length}%`,
|
||||
padding: 10,
|
||||
borderRadius: 3,
|
||||
backgroundColor: i === navigation.state.index ? 'tomato' : 'white',
|
||||
backgroundColor: i === state.index ? 'tomato' : 'white',
|
||||
}}
|
||||
>
|
||||
{descriptors[route.key].render()}
|
||||
|
||||
@@ -3,8 +3,9 @@ import { render } from 'react-dom';
|
||||
import {
|
||||
NavigationContainer,
|
||||
CompositeNavigationProp,
|
||||
NavigationHelpers,
|
||||
PartialState,
|
||||
NavigationHelpers,
|
||||
RouteProp,
|
||||
} from '../src';
|
||||
import Stack, { StackNavigationProp } from './StackNavigator';
|
||||
import Tab, { TabNavigationProp } from './TabNavigator';
|
||||
@@ -26,14 +27,16 @@ const MyTab = Tab<TabParamList>();
|
||||
|
||||
const First = ({
|
||||
navigation,
|
||||
route,
|
||||
}: {
|
||||
navigation: CompositeNavigationProp<
|
||||
StackNavigationProp<StackParamList, 'first'>,
|
||||
NavigationHelpers<TabParamList>
|
||||
>;
|
||||
route: RouteProp<StackParamList, 'first'>;
|
||||
}) => (
|
||||
<div>
|
||||
<h1>First, {navigation.state.params.author}</h1>
|
||||
<h1>First, {route.params.author}</h1>
|
||||
<button type="button" onClick={() => navigation.push('second')}>
|
||||
Push second
|
||||
</button>
|
||||
|
||||
@@ -7,7 +7,7 @@ export type ChildActionListener = (
|
||||
) => boolean;
|
||||
|
||||
const NavigationBuilderContext = React.createContext<{
|
||||
helpers?: NavigationHelpers;
|
||||
navigation?: NavigationHelpers;
|
||||
onAction?: (action: NavigationAction, sourceNavigatorKey?: string) => boolean;
|
||||
addActionListener?: (listener: ChildActionListener) => void;
|
||||
removeActionListener?: (listener: ChildActionListener) => void;
|
||||
|
||||
@@ -5,20 +5,20 @@ import {
|
||||
Route,
|
||||
NavigationState,
|
||||
NavigationHelpers,
|
||||
ScreenProps,
|
||||
RouteConfig,
|
||||
} from './types';
|
||||
import EnsureSingleNavigator from './EnsureSingleNavigator';
|
||||
|
||||
type Props = {
|
||||
screen: ScreenProps;
|
||||
helpers: NavigationHelpers;
|
||||
screen: RouteConfig;
|
||||
navigation: NavigationHelpers;
|
||||
route: Route & { state?: NavigationState };
|
||||
getState: () => NavigationState;
|
||||
setState: (state: NavigationState) => void;
|
||||
};
|
||||
|
||||
export default function SceneView(props: Props) {
|
||||
const { screen, route, helpers, getState, setState } = props;
|
||||
const { screen, route, navigation: helpers, getState, setState } = props;
|
||||
|
||||
const navigation = React.useMemo(
|
||||
() => ({
|
||||
@@ -35,9 +35,8 @@ export default function SceneView(props: Props) {
|
||||
),
|
||||
});
|
||||
},
|
||||
state: route,
|
||||
}),
|
||||
[getState, helpers, route, setState]
|
||||
[getState, helpers, route.key, setState]
|
||||
);
|
||||
|
||||
const getCurrentState = React.useCallback(() => {
|
||||
@@ -79,11 +78,12 @@ export default function SceneView(props: Props) {
|
||||
// @ts-ignore
|
||||
render={screen.component || screen.children}
|
||||
navigation={navigation}
|
||||
route={route}
|
||||
>
|
||||
{'component' in screen && screen.component !== undefined ? (
|
||||
<screen.component navigation={navigation} />
|
||||
<screen.component navigation={navigation} route={route} />
|
||||
) : 'children' in screen && screen.children !== undefined ? (
|
||||
screen.children({ navigation })
|
||||
screen.children({ navigation, route })
|
||||
) : null}
|
||||
</StaticContainer>
|
||||
</EnsureSingleNavigator>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ScreenProps } from './types';
|
||||
import { RouteConfig } from './types';
|
||||
|
||||
export default function Screen(_: ScreenProps) {
|
||||
export default function Screen(_: RouteConfig) {
|
||||
/* istanbul ignore next */
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -93,11 +93,9 @@ beforeEach(() => (MockRouter.key = 0));
|
||||
|
||||
it('initializes state for a navigator on navigation', () => {
|
||||
const TestNavigator = (props: any) => {
|
||||
const { navigation, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return descriptors[
|
||||
navigation.state.routes[navigation.state.index].key
|
||||
].render();
|
||||
return descriptors[state.routes[state.index].key].render();
|
||||
};
|
||||
|
||||
const FooScreen = (props: any) => {
|
||||
@@ -148,11 +146,9 @@ it('initializes state for a navigator on navigation', () => {
|
||||
|
||||
it('rehydrates state for a navigator on navigation', () => {
|
||||
const TestNavigator = (props: any) => {
|
||||
const { navigation, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return descriptors[
|
||||
navigation.state.routes[navigation.state.index].key
|
||||
].render();
|
||||
return descriptors[state.routes[state.index].key].render();
|
||||
};
|
||||
|
||||
const BarScreen = (props: any) => {
|
||||
@@ -195,11 +191,9 @@ it('rehydrates state for a navigator on navigation', () => {
|
||||
|
||||
it('initializes state for nested navigator on navigation', () => {
|
||||
const TestNavigator = (props: any) => {
|
||||
const { navigation, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return descriptors[
|
||||
navigation.state.routes[navigation.state.index].key
|
||||
].render();
|
||||
return descriptors[state.routes[state.index].key].render();
|
||||
};
|
||||
|
||||
const TestScreen = (props: any) => {
|
||||
@@ -255,11 +249,9 @@ it('initializes state for nested navigator on navigation', () => {
|
||||
|
||||
it("doesn't update state if nothing changed", () => {
|
||||
const TestNavigator = (props: any) => {
|
||||
const { navigation, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return descriptors[
|
||||
navigation.state.routes[navigation.state.index].key
|
||||
].render();
|
||||
return descriptors[state.routes[state.index].key].render();
|
||||
};
|
||||
|
||||
const FooScreen = (props: any) => {
|
||||
@@ -287,11 +279,9 @@ it("doesn't update state if nothing changed", () => {
|
||||
|
||||
it("doesn't update state if action wasn't handled", () => {
|
||||
const TestNavigator = (props: any) => {
|
||||
const { navigation, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return descriptors[
|
||||
navigation.state.routes[navigation.state.index].key
|
||||
].render();
|
||||
return descriptors[state.routes[state.index].key].render();
|
||||
};
|
||||
|
||||
const FooScreen = (props: any) => {
|
||||
@@ -319,11 +309,9 @@ it("doesn't update state if action wasn't handled", () => {
|
||||
|
||||
it('cleans up state when the navigator unmounts', () => {
|
||||
const TestNavigator = (props: any) => {
|
||||
const { navigation, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return descriptors[
|
||||
navigation.state.routes[navigation.state.index].key
|
||||
].render();
|
||||
return descriptors[state.routes[state.index].key].render();
|
||||
};
|
||||
|
||||
const FooScreen = (props: any) => {
|
||||
@@ -383,22 +371,15 @@ it("lets parent handle the action if child didn't", () => {
|
||||
};
|
||||
|
||||
const ParentNavigator = (props: any) => {
|
||||
const { navigation, descriptors } = useNavigationBuilder(
|
||||
ParentRouter,
|
||||
props
|
||||
);
|
||||
const { state, descriptors } = useNavigationBuilder(ParentRouter, props);
|
||||
|
||||
return descriptors[
|
||||
navigation.state.routes[navigation.state.index].key
|
||||
].render();
|
||||
return descriptors[state.routes[state.index].key].render();
|
||||
};
|
||||
|
||||
const ChildNavigator = (props: any) => {
|
||||
const { navigation, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return descriptors[
|
||||
navigation.state.routes[navigation.state.index].key
|
||||
].render();
|
||||
return descriptors[state.routes[state.index].key].render();
|
||||
};
|
||||
|
||||
const TestScreen = (props: any) => {
|
||||
@@ -444,11 +425,9 @@ it("lets parent handle the action if child didn't", () => {
|
||||
|
||||
it('allows arbitrary state updates by dispatching a function', () => {
|
||||
const TestNavigator = (props: any) => {
|
||||
const { navigation, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return descriptors[
|
||||
navigation.state.routes[navigation.state.index].key
|
||||
].render();
|
||||
return descriptors[state.routes[state.index].key].render();
|
||||
};
|
||||
|
||||
const FooScreen = (props: any) => {
|
||||
@@ -490,11 +469,9 @@ it('allows arbitrary state updates by dispatching a function', () => {
|
||||
|
||||
it('updates route params with setParams', () => {
|
||||
const TestNavigator = (props: any) => {
|
||||
const { navigation, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return descriptors[
|
||||
navigation.state.routes[navigation.state.index].key
|
||||
].render();
|
||||
return descriptors[state.routes[state.index].key].render();
|
||||
};
|
||||
|
||||
let setParams: (params: object) => void = () => undefined;
|
||||
|
||||
@@ -37,25 +37,17 @@ it("lets children handle the action if parent didn't", () => {
|
||||
};
|
||||
|
||||
const ChildNavigator = (props: any) => {
|
||||
const { navigation, descriptors } = useNavigationBuilder(
|
||||
ChildRouter,
|
||||
props
|
||||
);
|
||||
const { state, descriptors } = useNavigationBuilder(ChildRouter, props);
|
||||
|
||||
return descriptors[
|
||||
navigation.state.routes[navigation.state.index].key
|
||||
].render();
|
||||
return descriptors[state.routes[state.index].key].render();
|
||||
};
|
||||
|
||||
const ParentNavigator = (props: any) => {
|
||||
const { navigation, descriptors } = useNavigationBuilder(
|
||||
ParentRouter,
|
||||
props
|
||||
);
|
||||
const { state, descriptors } = useNavigationBuilder(ParentRouter, props);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{navigation.state.routes.map(route => descriptors[route.key].render())}
|
||||
{state.routes.map(route => descriptors[route.key].render())}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { ParamListBase, ScreenProps, TypedNavigator } from './types';
|
||||
import { ParamListBase, RouteConfig, TypedNavigator } from './types';
|
||||
import Screen from './Screen';
|
||||
|
||||
export default function createNavigator<N extends React.ComponentType<any>>(
|
||||
@@ -12,7 +12,7 @@ export default function createNavigator<N extends React.ComponentType<any>>(
|
||||
return {
|
||||
Navigator: RawNavigator,
|
||||
Screen: Screen as React.ComponentType<
|
||||
ScreenProps<ParamList, keyof ParamList>
|
||||
RouteConfig<ParamList, keyof ParamList>
|
||||
>,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -204,14 +204,6 @@ export type NavigationProp<
|
||||
ParamList extends ParamListBase,
|
||||
RouteName extends keyof ParamList
|
||||
> = NavigationHelpers<ParamList> & {
|
||||
/**
|
||||
* State for the child navigator.
|
||||
*/
|
||||
state: Omit<Route<RouteName>, 'params'> &
|
||||
(ParamList[RouteName] extends undefined
|
||||
? {}
|
||||
: { params: ParamList[RouteName] });
|
||||
|
||||
/**
|
||||
* Update the param object for the route.
|
||||
* The new params will be shallow merged with the old one.
|
||||
@@ -221,6 +213,19 @@ export type NavigationProp<
|
||||
setParams(params: ParamList[RouteName]): void;
|
||||
};
|
||||
|
||||
export type RouteProp<
|
||||
ParamList extends ParamListBase,
|
||||
RouteName extends keyof ParamList
|
||||
> = Omit<Route<RouteName>, 'params'> &
|
||||
(ParamList[RouteName] extends undefined
|
||||
? {}
|
||||
: {
|
||||
/**
|
||||
* Params for this route
|
||||
*/
|
||||
params: ParamList[RouteName];
|
||||
});
|
||||
|
||||
export type CompositeNavigationProp<
|
||||
A extends NavigationHelpers<ParamListBase>,
|
||||
B extends NavigationHelpers<ParamListBase>
|
||||
@@ -251,7 +256,7 @@ export type Options = {
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
export type ScreenProps<
|
||||
export type RouteConfig<
|
||||
ParamList extends ParamListBase = ParamListBase,
|
||||
RouteName extends keyof ParamList = string
|
||||
> = {
|
||||
@@ -295,5 +300,5 @@ export type TypedNavigator<
|
||||
initialRouteName?: keyof ParamList;
|
||||
}
|
||||
>;
|
||||
Screen: React.ComponentType<ScreenProps<ParamList, keyof ParamList>>;
|
||||
Screen: React.ComponentType<RouteConfig<ParamList, keyof ParamList>>;
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
NavigationHelpers,
|
||||
NavigationState,
|
||||
ParamListBase,
|
||||
ScreenProps,
|
||||
RouteConfig,
|
||||
} from './types';
|
||||
import SceneView from './SceneView';
|
||||
import NavigationBuilderContext, {
|
||||
@@ -15,8 +15,8 @@ import NavigationBuilderContext, {
|
||||
|
||||
type Options = {
|
||||
state: NavigationState | PartialState;
|
||||
screens: { [key: string]: ScreenProps<ParamListBase, string> };
|
||||
helpers: NavigationHelpers<ParamListBase>;
|
||||
screens: { [key: string]: RouteConfig<ParamListBase, string> };
|
||||
navigation: NavigationHelpers<ParamListBase>;
|
||||
onAction: (action: NavigationAction, sourceNavigatorKey?: string) => boolean;
|
||||
getState: () => NavigationState;
|
||||
setState: (state: NavigationState) => void;
|
||||
@@ -34,7 +34,7 @@ const EMPTY_OPTIONS = Object.freeze({});
|
||||
export default function useDescriptors({
|
||||
state,
|
||||
screens,
|
||||
helpers,
|
||||
navigation,
|
||||
onAction,
|
||||
getState,
|
||||
setState,
|
||||
@@ -44,13 +44,19 @@ export default function useDescriptors({
|
||||
}: Options) {
|
||||
const context = React.useMemo(
|
||||
() => ({
|
||||
helpers,
|
||||
navigation,
|
||||
onAction,
|
||||
addActionListener,
|
||||
removeActionListener,
|
||||
onChildUpdate,
|
||||
}),
|
||||
[helpers, onAction, onChildUpdate, addActionListener, removeActionListener]
|
||||
[
|
||||
navigation,
|
||||
onAction,
|
||||
onChildUpdate,
|
||||
addActionListener,
|
||||
removeActionListener,
|
||||
]
|
||||
);
|
||||
|
||||
return state.routes.reduce(
|
||||
@@ -62,7 +68,7 @@ export default function useDescriptors({
|
||||
return (
|
||||
<NavigationBuilderContext.Provider value={context}>
|
||||
<SceneView
|
||||
helpers={helpers}
|
||||
navigation={navigation}
|
||||
route={route}
|
||||
screen={screen}
|
||||
getState={getState}
|
||||
|
||||
@@ -5,7 +5,7 @@ import useRegisterNavigator from './useRegisterNavigator';
|
||||
import useDescriptors from './useDescriptors';
|
||||
import useNavigationHelpers from './useNavigationHelpers';
|
||||
import useOnAction from './useOnAction';
|
||||
import { Router, NavigationState, ScreenProps } from './types';
|
||||
import { Router, NavigationState, RouteConfig } from './types';
|
||||
import useOnChildUpdate from './useOnChildUpdate';
|
||||
import useChildActionListeners from './useChildActionListeners';
|
||||
|
||||
@@ -29,7 +29,7 @@ export default function useNavigationBuilder(
|
||||
}
|
||||
|
||||
if (React.isValidElement(child) && child.type === Screen) {
|
||||
return child.props as ScreenProps;
|
||||
return child.props as RouteConfig;
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
@@ -42,10 +42,10 @@ export default function useNavigationBuilder(
|
||||
.filter(Boolean)
|
||||
.reduce(
|
||||
(acc, curr) => {
|
||||
acc[curr!.name] = curr as ScreenProps;
|
||||
acc[curr!.name] = curr as RouteConfig;
|
||||
return acc;
|
||||
},
|
||||
{} as { [key: string]: ScreenProps }
|
||||
{} as { [key: string]: RouteConfig }
|
||||
);
|
||||
|
||||
const routeNames = Object.keys(screens);
|
||||
@@ -140,22 +140,17 @@ export default function useNavigationBuilder(
|
||||
setState,
|
||||
});
|
||||
|
||||
const helpers = useNavigationHelpers({
|
||||
const navigation = useNavigationHelpers({
|
||||
onAction,
|
||||
getState,
|
||||
setState,
|
||||
actionCreators: router.actionCreators,
|
||||
});
|
||||
|
||||
const navigation = React.useMemo(() => ({ ...helpers, state }), [
|
||||
helpers,
|
||||
state,
|
||||
]);
|
||||
|
||||
const descriptors = useDescriptors({
|
||||
state,
|
||||
screens,
|
||||
helpers,
|
||||
navigation,
|
||||
onAction,
|
||||
getState,
|
||||
setState,
|
||||
@@ -165,6 +160,7 @@ export default function useNavigationBuilder(
|
||||
});
|
||||
|
||||
return {
|
||||
state,
|
||||
navigation,
|
||||
descriptors,
|
||||
};
|
||||
|
||||
@@ -21,7 +21,7 @@ export default function useNavigationHelpers({
|
||||
setState,
|
||||
actionCreators,
|
||||
}: Options) {
|
||||
const { helpers: parentNavigationHelpers } = React.useContext(
|
||||
const { navigation: parentNavigationHelpers } = React.useContext(
|
||||
NavigationBuilderContext
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user