From 44909bd80aa33b62234ee028712611a2a5f25be2 Mon Sep 17 00:00:00 2001 From: "satyajit.happy" Date: Mon, 10 Jun 2019 01:29:06 +0200 Subject: [PATCH] refactor: minor tweaks --- README.md | 12 ++++----- example/StackNavigator.tsx | 21 ++++++++++----- example/TabNavigator.tsx | 16 ++++++----- example/index.html | 32 ++++++++++++++++++++++ example/index.tsx | 8 +++--- src/SceneView.tsx | 52 ++++++++++++++++++++++++++++++++++++ src/types.tsx | 8 +++--- src/useNavigationBuilder.tsx | 49 ++++++++++++--------------------- 8 files changed, 140 insertions(+), 58 deletions(-) create mode 100644 src/SceneView.tsx diff --git a/README.md b/README.md index e3b70db7..45b77b9a 100644 --- a/README.md +++ b/README.md @@ -20,18 +20,18 @@ Hook which can access the navigation state from the context. Along with the stat ### Router -An object that provides various actions to modify state as well as helpers. +An object that provides a reducer to update the state as well as some action creators. ### Navigator -Navigators bundle a `NavigationChild`, a `router` and a view which takes the navigation state and decides how to render it. +Navigators bundle a `router` and a view which takes the navigation state and decides how to render it. A simple navigator could look like this: ```js function StackNavigator({ initialRouteName, children, ...rest }) { // The `navigation` object contains the navigation state and some helpers (e.g. push, pop) - // The `descriptors` object contains `navigation` objects for children routes and helper for rendering a screen + // The `descriptors` object contains the screen options and a helper for rendering a screen const { navigation, descriptors } = useNavigationBuilder(StackRouter, { initialRouteName, children, @@ -44,12 +44,12 @@ function StackNavigator({ initialRouteName, children, ...rest }) { } ``` -The navigator can render a screen by calling `descriptors[route.key].render()`. Internally, the descriptor wraps the screen in a `NavigationProvider` to support nested state: +The navigator can render a screen by calling `descriptors[route.key].render()`. Internally, the descriptor wraps the screen in a `NavigationStateContext.Provider` to support nested state: ```js - + - + ``` ## Basic usage diff --git a/example/StackNavigator.tsx b/example/StackNavigator.tsx index fe260aa2..be176c60 100644 --- a/example/StackNavigator.tsx +++ b/example/StackNavigator.tsx @@ -24,10 +24,13 @@ type Action = export type StackNavigationProp = NavigationProp; const StackRouter = { - getInitialState( - routeNames: string[], - { initialRouteName = routeNames[0] }: { initialRouteName?: string } - ) { + initial({ + routeNames, + initialRouteName = routeNames[0], + }: { + routeNames: string[]; + initialRouteName?: string; + }) { const index = routeNames.indexOf(initialRouteName); return { @@ -88,10 +91,16 @@ export default function StackNavigator(props: Props) { key={route.key} style={{ position: 'absolute', - top: i * 10, + margin: 20, + left: i * 20, + top: i * 20, padding: 10, + height: 480, + width: 320, backgroundColor: 'white', - border: '1px solid black', + borderRadius: 3, + boxShadow: + '0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23)', }} > {descriptors[route.key].render()} diff --git a/example/TabNavigator.tsx b/example/TabNavigator.tsx index 206cfd76..f9dea177 100644 --- a/example/TabNavigator.tsx +++ b/example/TabNavigator.tsx @@ -22,10 +22,13 @@ type Action = { export type TabNavigationProp = NavigationProp; const TabRouter = { - getInitialState( - routeNames: string[], - { initialRouteName = routeNames[0] }: { initialRouteName?: string } - ) { + initial({ + routeNames, + initialRouteName = routeNames[0], + }: { + routeNames: string[]; + initialRouteName?: string; + }) { const index = routeNames.indexOf(initialRouteName); return { @@ -64,14 +67,15 @@ export default function TabNavigator(props: Props) { const { navigation, descriptors } = useNavigationBuilder(TabRouter, props); return ( -
+
{navigation.state.routes.map((route, i, self) => (
{descriptors[route.key].render()} diff --git a/example/index.html b/example/index.html index 9d984832..2be5e478 100644 --- a/example/index.html +++ b/example/index.html @@ -1,2 +1,34 @@ +
diff --git a/example/index.tsx b/example/index.tsx index be02b828..9863501d 100644 --- a/example/index.tsx +++ b/example/index.tsx @@ -7,7 +7,7 @@ import TabNavigator, { TabNavigationProp } from './TabNavigator'; const First = ({ navigation }: { navigation: StackNavigationProp }) => (
-

First route

+

First

@@ -22,7 +22,7 @@ const First = ({ navigation }: { navigation: StackNavigationProp }) => ( const Second = ({ navigation }: { navigation: StackNavigationProp }) => (
-

Second route

+

Second

@@ -38,7 +38,7 @@ const Fourth = ({ navigation: TabNavigationProp & StackNavigationProp; }) => (
-

Fourth route

+

Fourth

@@ -57,7 +57,7 @@ const Fifth = ({ navigation: TabNavigationProp & StackNavigationProp; }) => (
-

Fifth route

+

Fifth

diff --git a/src/SceneView.tsx b/src/SceneView.tsx new file mode 100644 index 00000000..a6a3c68f --- /dev/null +++ b/src/SceneView.tsx @@ -0,0 +1,52 @@ +import * as React from 'react'; +import { NavigationStateContext } from './NavigationContainer'; +import { NavigationPropContext } from './useNavigationBuilder'; +import { Route, NavigationProp, NavigationState } from './types'; + +type Props = { + screen: + | { component: React.ComponentType } + | { children: (props: any) => React.ReactNode }; + navigation: NavigationProp; + route: Route & { state?: NavigationState }; + state: NavigationState; + setState: (state: NavigationState) => void; +}; + +export default function SceneView(props: Props) { + const { screen, route, state, setState } = props; + + const navigation = React.useMemo( + () => ({ + ...props.navigation, + state: route, + }), + [props.navigation, route] + ); + + const value = React.useMemo( + () => ({ + state: route.state, + setState: (child: NavigationState) => + setState({ + ...state, + routes: state.routes.map(r => + r === route ? { ...route, state: child } : r + ), + }), + }), + [route, setState, state] + ); + + return ( + + + {'component' in screen && screen.component !== undefined ? ( + + ) : 'children' in screen && screen.children !== undefined ? ( + screen.children({ navigation }) + ) : null} + + + ); +} diff --git a/src/types.tsx b/src/types.tsx index 9f23ef98..38775eae 100644 --- a/src/types.tsx +++ b/src/types.tsx @@ -14,10 +14,10 @@ export type NavigationAction = { }; export type Router = { - getInitialState( - routeNames: string[], - options: { initialRouteName?: string } - ): NavigationState; + initial(options: { + routeNames: string[]; + initialRouteName?: string; + }): NavigationState; reduce(state: NavigationState, action: Action): NavigationState; actions: { [key: string]: (...args: any) => Action }; }; diff --git a/src/useNavigationBuilder.tsx b/src/useNavigationBuilder.tsx index 2fea8188..84041a1e 100644 --- a/src/useNavigationBuilder.tsx +++ b/src/useNavigationBuilder.tsx @@ -8,15 +8,16 @@ import { NavigationState, } from './types'; import Screen, { Props as ScreenProps } from './Screen'; +import SceneView from './SceneView'; type Options = { initialRouteName?: string; children: React.ReactElement[]; }; -const NavigationPropContext = React.createContext( - undefined -); +export const NavigationPropContext = React.createContext< + NavigationProp | undefined +>(undefined); export default function useNavigationBuilder(router: Router, options: Options) { const screens = React.Children.map(options.children, child => { @@ -35,10 +36,9 @@ export default function useNavigationBuilder(router: Router, options: Options) { {} as { [key: string]: ScreenProps } ); - const routeNames = Object.keys(screens); - const { - state = router.getInitialState(routeNames, { + state = router.initial({ + routeNames: Object.keys(screens), initialRouteName: options.initialRouteName, }), setState, @@ -69,34 +69,19 @@ export default function useNavigationBuilder(router: Router, options: Options) { const descriptors = state.routes.reduce( (acc, route) => { const screen = screens[route.name]; - const nav = { - ...navigation, - state: route, - }; acc[route.key] = { - render: () => ( - - setState({ - ...state, - routes: state.routes.map(r => - r === route ? { ...route, state: child } : r - ), - }), - }} - > - - {'component' in screen && screen.component !== undefined ? ( - - ) : 'children' in screen && screen.children !== undefined ? ( - screen.children({ navigation: nav }) - ) : null} - - - ), + render() { + return ( + + ); + }, options: screen.options || {}, }; return acc;