mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-03-06 17:34:59 +08:00
refactor: minor tweaks
This commit is contained in:
12
README.md
12
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
|
||||
<NavigationProvider state={route}>
|
||||
<NavigationStateContext.Provider state={route.state}>
|
||||
<MyComponent />
|
||||
</NavigationProvider>
|
||||
</NavigationStateContext.Provider>
|
||||
```
|
||||
|
||||
## Basic usage
|
||||
|
||||
@@ -24,10 +24,13 @@ type Action =
|
||||
export type StackNavigationProp = NavigationProp<typeof StackRouter>;
|
||||
|
||||
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()}
|
||||
|
||||
@@ -22,10 +22,13 @@ type Action = {
|
||||
export type TabNavigationProp = NavigationProp<typeof TabRouter>;
|
||||
|
||||
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 (
|
||||
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', height: '100%' }}>
|
||||
{navigation.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',
|
||||
border: '1px solid black',
|
||||
}}
|
||||
>
|
||||
{descriptors[route.key].render()}
|
||||
|
||||
@@ -1,2 +1,34 @@
|
||||
<style type="text/css">
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
*:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
*:focus-visible {
|
||||
outline: auto;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Source Sans Pro', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.42857143;
|
||||
background-color: #E2E1E0;
|
||||
}
|
||||
</style>
|
||||
<div id="root"></div>
|
||||
<script src="./index.tsx"></script>
|
||||
|
||||
@@ -7,7 +7,7 @@ import TabNavigator, { TabNavigationProp } from './TabNavigator';
|
||||
|
||||
const First = ({ navigation }: { navigation: StackNavigationProp }) => (
|
||||
<div>
|
||||
<h1>First route</h1>
|
||||
<h1>First</h1>
|
||||
<button type="button" onClick={() => navigation.push('second')}>
|
||||
Push second
|
||||
</button>
|
||||
@@ -22,7 +22,7 @@ const First = ({ navigation }: { navigation: StackNavigationProp }) => (
|
||||
|
||||
const Second = ({ navigation }: { navigation: StackNavigationProp }) => (
|
||||
<div>
|
||||
<h1>Second route</h1>
|
||||
<h1>Second</h1>
|
||||
<button type="button" onClick={() => navigation.push('first')}>
|
||||
Push first
|
||||
</button>
|
||||
@@ -38,7 +38,7 @@ const Fourth = ({
|
||||
navigation: TabNavigationProp & StackNavigationProp;
|
||||
}) => (
|
||||
<div>
|
||||
<h1>Fourth route</h1>
|
||||
<h1>Fourth</h1>
|
||||
<button type="button" onClick={() => navigation.jumpTo('fifth')}>
|
||||
Jump to fifth
|
||||
</button>
|
||||
@@ -57,7 +57,7 @@ const Fifth = ({
|
||||
navigation: TabNavigationProp & StackNavigationProp;
|
||||
}) => (
|
||||
<div>
|
||||
<h1>Fifth route</h1>
|
||||
<h1>Fifth</h1>
|
||||
<button type="button" onClick={() => navigation.jumpTo('fourth')}>
|
||||
Jump to fourth
|
||||
</button>
|
||||
|
||||
52
src/SceneView.tsx
Normal file
52
src/SceneView.tsx
Normal file
@@ -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<any> }
|
||||
| { 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 (
|
||||
<NavigationStateContext.Provider value={value}>
|
||||
<NavigationPropContext.Provider value={navigation}>
|
||||
{'component' in screen && screen.component !== undefined ? (
|
||||
<screen.component navigation={navigation} />
|
||||
) : 'children' in screen && screen.children !== undefined ? (
|
||||
screen.children({ navigation })
|
||||
) : null}
|
||||
</NavigationPropContext.Provider>
|
||||
</NavigationStateContext.Provider>
|
||||
);
|
||||
}
|
||||
@@ -14,10 +14,10 @@ export type NavigationAction = {
|
||||
};
|
||||
|
||||
export type Router<Action extends NavigationAction = NavigationAction> = {
|
||||
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 };
|
||||
};
|
||||
|
||||
@@ -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<NavigationProp | undefined>(
|
||||
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: () => (
|
||||
<NavigationStateContext.Provider
|
||||
value={{
|
||||
state: route.state,
|
||||
setState: child =>
|
||||
setState({
|
||||
...state,
|
||||
routes: state.routes.map(r =>
|
||||
r === route ? { ...route, state: child } : r
|
||||
),
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<NavigationPropContext.Provider value={nav}>
|
||||
{'component' in screen && screen.component !== undefined ? (
|
||||
<screen.component navigation={nav} />
|
||||
) : 'children' in screen && screen.children !== undefined ? (
|
||||
screen.children({ navigation: nav })
|
||||
) : null}
|
||||
</NavigationPropContext.Provider>
|
||||
</NavigationStateContext.Provider>
|
||||
),
|
||||
render() {
|
||||
return (
|
||||
<SceneView
|
||||
navigation={navigation}
|
||||
route={route}
|
||||
screen={screen}
|
||||
state={state}
|
||||
setState={setState}
|
||||
/>
|
||||
);
|
||||
},
|
||||
options: screen.options || {},
|
||||
};
|
||||
return acc;
|
||||
|
||||
Reference in New Issue
Block a user