mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-02-11 09:20:54 +08:00
refactor: drop getStateForChildUpdate in favor of getStateForRouteFocus (#15)
This commit is contained in:
committed by
Michał Osadnik
parent
3d8ba13135
commit
44b2ace9ee
@@ -96,6 +96,20 @@ const StackRouter: Router<CommonAction | Action> = {
|
||||
};
|
||||
},
|
||||
|
||||
getStateForRouteFocus(state, key) {
|
||||
const index = state.routes.findIndex(r => r.key === key);
|
||||
|
||||
if (index === -1 || index === state.index) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
index,
|
||||
routes: state.routes.slice(0, index + 1),
|
||||
};
|
||||
},
|
||||
|
||||
getStateForAction(state, action) {
|
||||
switch (action.type) {
|
||||
case 'PUSH':
|
||||
@@ -219,27 +233,6 @@ const StackRouter: Router<CommonAction | Action> = {
|
||||
}
|
||||
},
|
||||
|
||||
getStateForChildUpdate(state, { update, focus, key }) {
|
||||
const index = state.routes.findIndex(r => r.key === key);
|
||||
|
||||
if (index === -1) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
index: focus ? index : state.index,
|
||||
routes: focus
|
||||
? [
|
||||
...state.routes.slice(0, index),
|
||||
{ ...state.routes[index], state: update },
|
||||
]
|
||||
: state.routes.map((route, i) =>
|
||||
i === index ? { ...route, state: update } : route
|
||||
),
|
||||
};
|
||||
},
|
||||
|
||||
shouldActionPropagateToChildren(action) {
|
||||
return action.type === 'NAVIGATE';
|
||||
},
|
||||
|
||||
@@ -87,6 +87,16 @@ const TabRouter: Router<Action | CommonAction> = {
|
||||
};
|
||||
},
|
||||
|
||||
getStateForRouteFocus(state, key) {
|
||||
const index = state.routes.findIndex(r => r.key === key);
|
||||
|
||||
if (index === -1) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return { ...state, index };
|
||||
},
|
||||
|
||||
getStateForAction(state, action) {
|
||||
switch (action.type) {
|
||||
case 'JUMP_TO':
|
||||
@@ -152,22 +162,6 @@ const TabRouter: Router<Action | CommonAction> = {
|
||||
}
|
||||
},
|
||||
|
||||
getStateForChildUpdate(state, { update, focus, key }) {
|
||||
const index = state.routes.findIndex(r => r.key === key);
|
||||
|
||||
if (index === -1) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
index: focus ? index : state.index,
|
||||
routes: state.routes.map((route, i) =>
|
||||
i === index ? { ...route, state: update } : route
|
||||
),
|
||||
};
|
||||
},
|
||||
|
||||
shouldActionPropagateToChildren(action) {
|
||||
return action.type === 'NAVIGATE';
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { NavigationHelpers, NavigationAction, NavigationState } from './types';
|
||||
import { NavigationHelpers, NavigationAction } from './types';
|
||||
|
||||
export type ChildActionListener = (
|
||||
action: NavigationAction,
|
||||
@@ -11,11 +11,7 @@ const NavigationBuilderContext = React.createContext<{
|
||||
onAction?: (action: NavigationAction, sourceNavigatorKey?: string) => boolean;
|
||||
addActionListener?: (listener: ChildActionListener) => void;
|
||||
removeActionListener?: (listener: ChildActionListener) => void;
|
||||
onChildUpdate?: (
|
||||
state: NavigationState,
|
||||
focus: boolean,
|
||||
key: string | undefined
|
||||
) => void;
|
||||
onRouteFocus?: (key: string) => void;
|
||||
}>({});
|
||||
|
||||
export default NavigationBuilderContext;
|
||||
|
||||
81
src/__tests__/__fixtures__/MockRouter.tsx
Normal file
81
src/__tests__/__fixtures__/MockRouter.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import { Router } from '../../types';
|
||||
|
||||
const MockRouter: Router<{ type: string }> & { key: number } = {
|
||||
key: 0,
|
||||
|
||||
getInitialState({
|
||||
routeNames,
|
||||
initialRouteName = routeNames[0],
|
||||
initialParamsList,
|
||||
}) {
|
||||
const index = routeNames.indexOf(initialRouteName);
|
||||
|
||||
return {
|
||||
key: String(MockRouter.key++),
|
||||
index,
|
||||
routeNames,
|
||||
routes: routeNames.map(name => ({
|
||||
name,
|
||||
key: name,
|
||||
params: initialParamsList[name],
|
||||
})),
|
||||
};
|
||||
},
|
||||
|
||||
getRehydratedState({ routeNames, partialState }) {
|
||||
let state = partialState;
|
||||
|
||||
if (state.routeNames === undefined || state.key === undefined) {
|
||||
state = {
|
||||
...state,
|
||||
routeNames,
|
||||
key: String(MockRouter.key++),
|
||||
};
|
||||
}
|
||||
|
||||
return state;
|
||||
},
|
||||
|
||||
getStateForRouteNamesChange(state, { routeNames }) {
|
||||
return {
|
||||
...state,
|
||||
routeNames,
|
||||
routes: state.routes.filter(route => routeNames.includes(route.name)),
|
||||
};
|
||||
},
|
||||
|
||||
getStateForRouteFocus(state, key) {
|
||||
const index = state.routes.findIndex(r => r.key === key);
|
||||
|
||||
if (index === -1 || index === state.index) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return { ...state, index };
|
||||
},
|
||||
|
||||
getStateForAction(state, action) {
|
||||
switch (action.type) {
|
||||
case 'UPDATE':
|
||||
return { ...state };
|
||||
|
||||
case 'NOOP':
|
||||
return state;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
shouldActionPropagateToChildren() {
|
||||
return false;
|
||||
},
|
||||
|
||||
shouldActionChangeFocus() {
|
||||
return false;
|
||||
},
|
||||
|
||||
actionCreators: {},
|
||||
};
|
||||
|
||||
export default MockRouter;
|
||||
@@ -3,91 +3,7 @@ import { render, act } from 'react-native-testing-library';
|
||||
import Screen from '../Screen';
|
||||
import NavigationContainer from '../NavigationContainer';
|
||||
import useNavigationBuilder from '../useNavigationBuilder';
|
||||
import { Router } from '../types';
|
||||
|
||||
export const MockRouter: Router<{ type: string }> & { key: number } = {
|
||||
key: 0,
|
||||
|
||||
getInitialState({
|
||||
routeNames,
|
||||
initialRouteName = routeNames[0],
|
||||
initialParamsList,
|
||||
}) {
|
||||
const index = routeNames.indexOf(initialRouteName);
|
||||
|
||||
return {
|
||||
key: String(MockRouter.key++),
|
||||
index,
|
||||
routeNames,
|
||||
routes: routeNames.map(name => ({
|
||||
name,
|
||||
key: name,
|
||||
params: initialParamsList[name],
|
||||
})),
|
||||
};
|
||||
},
|
||||
|
||||
getRehydratedState({ routeNames, partialState }) {
|
||||
let state = partialState;
|
||||
|
||||
if (state.routeNames === undefined || state.key === undefined) {
|
||||
state = {
|
||||
...state,
|
||||
routeNames,
|
||||
key: String(MockRouter.key++),
|
||||
};
|
||||
}
|
||||
|
||||
return state;
|
||||
},
|
||||
|
||||
getStateForRouteNamesChange(state, { routeNames }) {
|
||||
return {
|
||||
...state,
|
||||
routeNames,
|
||||
routes: state.routes.filter(route => routeNames.includes(route.name)),
|
||||
};
|
||||
},
|
||||
|
||||
getStateForAction(state, action) {
|
||||
switch (action.type) {
|
||||
case 'UPDATE':
|
||||
return { ...state };
|
||||
|
||||
case 'NOOP':
|
||||
return state;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
getStateForChildUpdate(state, { update, focus, key }) {
|
||||
const index = state.routes.findIndex(r => r.key === key);
|
||||
|
||||
if (index === -1) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
index: focus ? index : state.index,
|
||||
routes: state.routes.map((route, i) =>
|
||||
i === index ? { ...route, state: update } : route
|
||||
),
|
||||
};
|
||||
},
|
||||
|
||||
shouldActionPropagateToChildren() {
|
||||
return false;
|
||||
},
|
||||
|
||||
shouldActionChangeFocus() {
|
||||
return false;
|
||||
},
|
||||
|
||||
actionCreators: {},
|
||||
};
|
||||
import MockRouter from './__fixtures__/MockRouter';
|
||||
|
||||
beforeEach(() => (MockRouter.key = 0));
|
||||
|
||||
@@ -399,75 +315,6 @@ it('cleans up state when the navigator unmounts', () => {
|
||||
expect(onStateChange).lastCalledWith(undefined);
|
||||
});
|
||||
|
||||
it("lets parent handle the action if child didn't", () => {
|
||||
const ParentRouter: Router<{ type: string }> = {
|
||||
...MockRouter,
|
||||
|
||||
getStateForAction(state, action) {
|
||||
if (action.type === 'REVERSE') {
|
||||
return {
|
||||
...state,
|
||||
routes: state.routes.slice().reverse(),
|
||||
};
|
||||
}
|
||||
|
||||
return MockRouter.getStateForAction(state, action);
|
||||
},
|
||||
};
|
||||
|
||||
const ParentNavigator = (props: any) => {
|
||||
const { state, descriptors } = useNavigationBuilder(ParentRouter, props);
|
||||
|
||||
return descriptors[state.routes[state.index].key].render();
|
||||
};
|
||||
|
||||
const ChildNavigator = (props: any) => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return descriptors[state.routes[state.index].key].render();
|
||||
};
|
||||
|
||||
const TestScreen = (props: any) => {
|
||||
React.useEffect(() => {
|
||||
props.navigation.dispatch({ type: 'REVERSE' });
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
render(
|
||||
<NavigationContainer onStateChange={onStateChange}>
|
||||
<ParentNavigator initialRouteName="baz">
|
||||
<Screen name="foo">{() => null}</Screen>
|
||||
<Screen name="bar">{() => null}</Screen>
|
||||
<Screen name="baz">
|
||||
{() => (
|
||||
<ChildNavigator>
|
||||
<Screen name="qux" component={TestScreen} />
|
||||
</ChildNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
</ParentNavigator>
|
||||
</NavigationContainer>
|
||||
);
|
||||
|
||||
expect(onStateChange).toBeCalledTimes(1);
|
||||
expect(onStateChange).lastCalledWith({
|
||||
index: 2,
|
||||
key: '0',
|
||||
routeNames: ['foo', 'bar', 'baz'],
|
||||
routes: [
|
||||
{ key: 'baz', name: 'baz' },
|
||||
{ key: 'bar', name: 'bar' },
|
||||
{ key: 'foo', name: 'foo' },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('allows arbitrary state updates by dispatching a function', () => {
|
||||
const TestNavigator = (props: any) => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
@@ -4,10 +4,79 @@ import { Router } from '../types';
|
||||
import useNavigationBuilder from '../useNavigationBuilder';
|
||||
import NavigationContainer from '../NavigationContainer';
|
||||
import Screen from '../Screen';
|
||||
import { MockRouter } from './index.test';
|
||||
import MockRouter from './__fixtures__/MockRouter';
|
||||
|
||||
beforeEach(() => (MockRouter.key = 0));
|
||||
|
||||
it("lets parent handle the action if child didn't", () => {
|
||||
const ParentRouter: Router<{ type: string }> = {
|
||||
...MockRouter,
|
||||
|
||||
getStateForAction(state, action) {
|
||||
if (action.type === 'REVERSE') {
|
||||
return {
|
||||
...state,
|
||||
routes: state.routes.slice().reverse(),
|
||||
};
|
||||
}
|
||||
|
||||
return MockRouter.getStateForAction(state, action);
|
||||
},
|
||||
};
|
||||
|
||||
const ParentNavigator = (props: any) => {
|
||||
const { state, descriptors } = useNavigationBuilder(ParentRouter, props);
|
||||
|
||||
return descriptors[state.routes[state.index].key].render();
|
||||
};
|
||||
|
||||
const ChildNavigator = (props: any) => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return descriptors[state.routes[state.index].key].render();
|
||||
};
|
||||
|
||||
const TestScreen = (props: any) => {
|
||||
React.useEffect(() => {
|
||||
props.navigation.dispatch({ type: 'REVERSE' });
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
render(
|
||||
<NavigationContainer onStateChange={onStateChange}>
|
||||
<ParentNavigator initialRouteName="baz">
|
||||
<Screen name="foo">{() => null}</Screen>
|
||||
<Screen name="bar">{() => null}</Screen>
|
||||
<Screen name="baz">
|
||||
{() => (
|
||||
<ChildNavigator>
|
||||
<Screen name="qux" component={TestScreen} />
|
||||
</ChildNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
</ParentNavigator>
|
||||
</NavigationContainer>
|
||||
);
|
||||
|
||||
expect(onStateChange).toBeCalledTimes(1);
|
||||
expect(onStateChange).lastCalledWith({
|
||||
index: 2,
|
||||
key: '0',
|
||||
routeNames: ['foo', 'bar', 'baz'],
|
||||
routes: [
|
||||
{ key: 'baz', name: 'baz' },
|
||||
{ key: 'bar', name: 'bar' },
|
||||
{ key: 'foo', name: 'foo' },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("lets children handle the action if parent didn't", () => {
|
||||
const ParentRouter: Router<{ type: string }> = {
|
||||
...MockRouter,
|
||||
@@ -92,6 +92,14 @@ export type Router<Action extends NavigationAction = CommonAction> = {
|
||||
}
|
||||
): NavigationState;
|
||||
|
||||
/**
|
||||
* Take the current state and key of a route, and return a new state with the route focused
|
||||
*
|
||||
* @param state State object to apply the action on.
|
||||
* @param key Key of the route to focus.
|
||||
*/
|
||||
getStateForRouteFocus(state: NavigationState, key: string): NavigationState;
|
||||
|
||||
/**
|
||||
* Take the current state and action, and return a new state.
|
||||
* If the action cannot be handled, return `null`.
|
||||
@@ -104,23 +112,6 @@ export type Router<Action extends NavigationAction = CommonAction> = {
|
||||
action: Action
|
||||
): NavigationState | null;
|
||||
|
||||
/**
|
||||
* Update state for a child navigator and focus it
|
||||
*
|
||||
* @param state State object to apply the action on.
|
||||
* @param options.update Updated navigation state for the child navigator.
|
||||
* @param options.focus Whether to focus the new child.
|
||||
* @param options.key Route key of the child to update.
|
||||
*/
|
||||
getStateForChildUpdate(
|
||||
state: NavigationState,
|
||||
payload: {
|
||||
update: NavigationState;
|
||||
focus: boolean;
|
||||
key: string | undefined;
|
||||
}
|
||||
): NavigationState;
|
||||
|
||||
/**
|
||||
* Whether the action bubbles to other navigators
|
||||
* When an action isn't handled by current navigator, it can be passed to nested navigators
|
||||
|
||||
@@ -22,11 +22,7 @@ type Options = {
|
||||
setState: (state: NavigationState) => void;
|
||||
addActionListener: (listener: ChildActionListener) => void;
|
||||
removeActionListener: (listener: ChildActionListener) => void;
|
||||
onChildUpdate: (
|
||||
state: NavigationState,
|
||||
focus: boolean,
|
||||
key: string | undefined
|
||||
) => void;
|
||||
onRouteFocus: (key: string) => void;
|
||||
};
|
||||
|
||||
const EMPTY_OPTIONS = Object.freeze({});
|
||||
@@ -40,7 +36,7 @@ export default function useDescriptors({
|
||||
setState,
|
||||
addActionListener,
|
||||
removeActionListener,
|
||||
onChildUpdate,
|
||||
onRouteFocus,
|
||||
}: Options) {
|
||||
const context = React.useMemo(
|
||||
() => ({
|
||||
@@ -48,12 +44,12 @@ export default function useDescriptors({
|
||||
onAction,
|
||||
addActionListener,
|
||||
removeActionListener,
|
||||
onChildUpdate,
|
||||
onRouteFocus,
|
||||
}),
|
||||
[
|
||||
navigation,
|
||||
onAction,
|
||||
onChildUpdate,
|
||||
onRouteFocus,
|
||||
addActionListener,
|
||||
removeActionListener,
|
||||
]
|
||||
|
||||
@@ -6,7 +6,7 @@ import useDescriptors from './useDescriptors';
|
||||
import useNavigationHelpers from './useNavigationHelpers';
|
||||
import useOnAction from './useOnAction';
|
||||
import { Router, NavigationState, RouteConfig } from './types';
|
||||
import useOnChildUpdate from './useOnChildUpdate';
|
||||
import useOnRouteFocus from './useOnRouteFocus';
|
||||
import useChildActionListeners from './useChildActionListeners';
|
||||
|
||||
type Options = {
|
||||
@@ -138,11 +138,10 @@ export default function useNavigationBuilder(
|
||||
getState,
|
||||
setState,
|
||||
key,
|
||||
getStateForAction: router.getStateForAction,
|
||||
actionListeners,
|
||||
listeners: actionListeners,
|
||||
});
|
||||
|
||||
const onChildUpdate = useOnChildUpdate({
|
||||
const onRouteFocus = useOnRouteFocus({
|
||||
router,
|
||||
onAction,
|
||||
key,
|
||||
@@ -164,7 +163,7 @@ export default function useNavigationBuilder(
|
||||
onAction,
|
||||
getState,
|
||||
setState,
|
||||
onChildUpdate,
|
||||
onRouteFocus,
|
||||
addActionListener,
|
||||
removeActionListener,
|
||||
});
|
||||
|
||||
@@ -5,15 +5,11 @@ import NavigationBuilderContext, {
|
||||
import { NavigationAction, NavigationState, Router } from './types';
|
||||
|
||||
type Options = {
|
||||
router: Router;
|
||||
getState: () => NavigationState;
|
||||
router: Router<NavigationAction>;
|
||||
key?: string;
|
||||
getState: () => NavigationState;
|
||||
setState: (state: NavigationState) => void;
|
||||
getStateForAction: (
|
||||
state: NavigationState,
|
||||
action: NavigationAction
|
||||
) => NavigationState | null;
|
||||
actionListeners: ChildActionListener[];
|
||||
listeners: ChildActionListener[];
|
||||
};
|
||||
|
||||
export default function useOnAction({
|
||||
@@ -21,12 +17,11 @@ export default function useOnAction({
|
||||
getState,
|
||||
setState,
|
||||
key,
|
||||
getStateForAction,
|
||||
actionListeners,
|
||||
listeners,
|
||||
}: Options) {
|
||||
const {
|
||||
onAction: handleActionParent,
|
||||
onChildUpdate: handleChildUpdateParent,
|
||||
onAction: onActionParent,
|
||||
onRouteFocus: onRouteFocusParent,
|
||||
} = React.useContext(NavigationBuilderContext);
|
||||
|
||||
return React.useCallback(
|
||||
@@ -37,30 +32,34 @@ export default function useOnAction({
|
||||
return false;
|
||||
}
|
||||
|
||||
const result = getStateForAction(state, action);
|
||||
const result = router.getStateForAction(state, action);
|
||||
|
||||
if (result !== null) {
|
||||
if (handleChildUpdateParent) {
|
||||
if (state !== result) {
|
||||
setState(result);
|
||||
}
|
||||
|
||||
if (onRouteFocusParent !== undefined) {
|
||||
const shouldFocus = router.shouldActionChangeFocus(action);
|
||||
|
||||
handleChildUpdateParent(result, shouldFocus, key);
|
||||
} else if (state !== result) {
|
||||
setState(result);
|
||||
if (shouldFocus && key !== undefined) {
|
||||
onRouteFocusParent(key);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (handleActionParent !== undefined) {
|
||||
if (onActionParent !== undefined) {
|
||||
// Bubble action to the parent if the current navigator didn't handle it
|
||||
if (handleActionParent(action, state.key)) {
|
||||
if (onActionParent(action, state.key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (router.shouldActionPropagateToChildren(action)) {
|
||||
for (let i = actionListeners.length - 1; i >= 0; i--) {
|
||||
const listener = actionListeners[i];
|
||||
for (let i = listeners.length - 1; i >= 0; i--) {
|
||||
const listener = listeners[i];
|
||||
|
||||
if (listener(action, state.key)) {
|
||||
return true;
|
||||
@@ -72,13 +71,12 @@ export default function useOnAction({
|
||||
},
|
||||
[
|
||||
getState,
|
||||
getStateForAction,
|
||||
handleActionParent,
|
||||
router,
|
||||
handleChildUpdateParent,
|
||||
key,
|
||||
onActionParent,
|
||||
onRouteFocusParent,
|
||||
setState,
|
||||
actionListeners,
|
||||
key,
|
||||
listeners,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import { NavigationAction, NavigationState, Router } from './types';
|
||||
import NavigationBuilderContext from './NavigationBuilderContext';
|
||||
|
||||
type Options = {
|
||||
router: Router;
|
||||
onAction: (action: NavigationAction, sourceNavigatorKey?: string) => boolean;
|
||||
getState: () => NavigationState;
|
||||
setState: (state: NavigationState) => void;
|
||||
key?: string;
|
||||
};
|
||||
|
||||
export default function useOnChildUpdate({
|
||||
router,
|
||||
onAction,
|
||||
getState,
|
||||
key: sourceNavigatorKey,
|
||||
setState,
|
||||
}: Options) {
|
||||
const {
|
||||
onChildUpdate: parentOnChildUpdate,
|
||||
addActionListener: parentAddActionListener,
|
||||
removeActionListener: parentRemoveActionListener,
|
||||
} = React.useContext(NavigationBuilderContext);
|
||||
|
||||
React.useEffect(() => {
|
||||
parentAddActionListener && parentAddActionListener(onAction);
|
||||
|
||||
return () => {
|
||||
parentRemoveActionListener && parentRemoveActionListener(onAction);
|
||||
};
|
||||
}, [onAction, parentAddActionListener, parentRemoveActionListener]);
|
||||
|
||||
const onChildUpdate = React.useCallback(
|
||||
(update: NavigationState, focus: boolean, key: string | undefined) => {
|
||||
const state = getState();
|
||||
const result = router.getStateForChildUpdate(state, {
|
||||
update,
|
||||
focus,
|
||||
key,
|
||||
});
|
||||
|
||||
if (parentOnChildUpdate !== undefined) {
|
||||
parentOnChildUpdate(result, focus, sourceNavigatorKey);
|
||||
} else {
|
||||
setState(result);
|
||||
}
|
||||
},
|
||||
[getState, parentOnChildUpdate, sourceNavigatorKey, router, setState]
|
||||
);
|
||||
|
||||
return onChildUpdate;
|
||||
}
|
||||
52
src/useOnRouteFocus.tsx
Normal file
52
src/useOnRouteFocus.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import * as React from 'react';
|
||||
import { NavigationAction, NavigationState, Router } from './types';
|
||||
import NavigationBuilderContext from './NavigationBuilderContext';
|
||||
|
||||
type Options = {
|
||||
router: Router;
|
||||
onAction: (action: NavigationAction, sourceNavigatorKey?: string) => boolean;
|
||||
getState: () => NavigationState;
|
||||
setState: (state: NavigationState) => void;
|
||||
key?: string;
|
||||
};
|
||||
|
||||
export default function useOnRouteFocus({
|
||||
router,
|
||||
onAction,
|
||||
getState,
|
||||
key: sourceNavigatorKey,
|
||||
setState,
|
||||
}: Options) {
|
||||
const {
|
||||
onRouteFocus: onRouteFocusParent,
|
||||
addActionListener: addActionListenerParent,
|
||||
removeActionListener: removeActionListenerParent,
|
||||
} = React.useContext(NavigationBuilderContext);
|
||||
|
||||
React.useEffect(() => {
|
||||
addActionListenerParent && addActionListenerParent(onAction);
|
||||
|
||||
return () => {
|
||||
removeActionListenerParent && removeActionListenerParent(onAction);
|
||||
};
|
||||
}, [addActionListenerParent, onAction, removeActionListenerParent]);
|
||||
|
||||
return React.useCallback(
|
||||
(key: string) => {
|
||||
const state = getState();
|
||||
const result = router.getStateForRouteFocus(state, key);
|
||||
|
||||
if (result !== state) {
|
||||
setState(result);
|
||||
}
|
||||
|
||||
if (
|
||||
onRouteFocusParent !== undefined &&
|
||||
sourceNavigatorKey !== undefined
|
||||
) {
|
||||
onRouteFocusParent(sourceNavigatorKey);
|
||||
}
|
||||
},
|
||||
[getState, onRouteFocusParent, router, setState, sourceNavigatorKey]
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user