Skip to content

Commit 05549af

Browse files
refactor: Migrate to uirouter/react 1.x hooks
BREAKING CHANGE: Requires uirouter/react 1.0.0 or higher
1 parent af4237d commit 05549af

File tree

5 files changed

+63
-102
lines changed

5 files changed

+63
-102
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
"license": "MIT",
1717
"dependencies": {
1818
"@uirouter/angularjs": "1.0.23",
19-
"@uirouter/core": "6.0.1",
20-
"@uirouter/react": "0.8.10"
19+
"@uirouter/core": "6.0.4",
20+
"@uirouter/react": "1.0.1"
2121
},
2222
"peerDependencies": {
2323
"angular": "^1.5.0",

src/angularjs/ReactUIViewAdapterComponent.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ hybridModule.directive('reactUiViewAdapter', function() {
4242
debug('.link()', 'linking react-ui-view-adapter into ', el, attrs);
4343

4444
// The UIView ref callback, which is called after the initial render
45+
// the ref value will be the component instance
4546
const ref = (ref: HTMLElement) => {
4647
// If refs are the same - don't re-render React component.
4748
const isSameRef = ref && _ref === ref;
@@ -73,8 +74,8 @@ hybridModule.directive('reactUiViewAdapter', function() {
7374
};
7475

7576
const provideContextToAngularJSChildren = () => {
76-
const $cfg = _ref && _ref.uiViewData && _ref.uiViewData.config;
77-
const $uiView = _ref && _ref.uiViewAddress;
77+
const $cfg = _ref?.uiViewData?.config;
78+
const $uiView = _ref?.uiViewAddress;
7879

7980
debug('.provideContextToAngularJSChildren', '', el, $cfg, $uiView);
8081

src/react/ReactUIView.tsx

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from 'react';
2-
import { UIRouterConsumer, UIView, UIViewConsumer, UIViewProps } from '@uirouter/react';
2+
import { UIView, UIViewProps } from '@uirouter/react';
33
import { UIRouterContextComponent } from './UIRouterReactContext';
44
import { debugLog } from '../debug';
55

@@ -11,18 +11,9 @@ export interface IReactUIViewProps extends UIViewProps {
1111

1212
export const ReactUIView = ({ refFn, ...props }: IReactUIViewProps) => {
1313
debugLog('react', 'ReactUIView', `?/${props['name']}`, '.render()', '');
14-
1514
return (
1615
<UIRouterContextComponent parentContextLevel="3" inherited={false}>
17-
<UIRouterConsumer>
18-
{router => (
19-
<UIViewConsumer>
20-
{parentUiView => (
21-
<InternalUIView {...props} ref={refFn as any} parentUIView={parentUiView} router={router} />
22-
)}
23-
</UIViewConsumer>
24-
)}
25-
</UIRouterConsumer>
16+
<InternalUIView {...props} ref={refFn as any} />
2617
</UIRouterContextComponent>
2718
);
2819
};

src/react/UIRouterReactContext.tsx

Lines changed: 55 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,82 @@
1-
import { UIViewData } from '@uirouter/angularjs/lib/directives/viewDirective';
2-
import { UIRouterConsumer, UIRouterProvider, UIRouterReact, UIViewConsumer, UIViewProvider } from '@uirouter/react';
31
import * as angular from 'angular';
42
import * as React from 'react';
5-
import { ReactElement } from 'react';
6-
import IInjectorService = angular.auto.IInjectorService;
3+
import { ReactNode, useContext, useRef, useState } from 'react';
4+
import { UIViewData } from '@uirouter/angularjs/lib/directives/viewDirective';
5+
import { UIRouterContext, UIViewContext, UIViewAddress } from '@uirouter/react';
6+
import { UIRouter } from '@uirouter/core';
77

88
export interface IUIRouterContextComponentProps {
99
parentContextLevel?: string;
1010
inherited?: boolean;
1111
}
1212

13-
export interface IUIRouterContextComponentState {
14-
ready: boolean;
15-
router?: UIRouterReact;
16-
parentUIViewAddress?: any;
13+
interface IContextFromAngularJS {
14+
router: UIRouter;
15+
addr: UIViewAddress;
1716
}
17+
const initialState: IContextFromAngularJS = { router: undefined, addr: undefined };
1818

1919
/**
2020
* Provide react context necessary for UIView, UISref and UISrefActive
2121
*
2222
* Gets the context from the parent react UIView (if component tree is all react)
2323
* Gets the context from the from parent angular ui-view if no parent reat UIView is available
2424
*/
25-
export class UIRouterContextComponent extends React.Component<
26-
IUIRouterContextComponentProps,
27-
IUIRouterContextComponentState
28-
> {
29-
public static defaultProps: Partial<IUIRouterContextComponentProps> = {
30-
parentContextLevel: '0',
31-
inherited: true,
32-
};
33-
34-
public state: IUIRouterContextComponentState = {
35-
ready: false,
36-
router: null,
37-
parentUIViewAddress: null,
38-
};
39-
40-
private ref: HTMLElement;
41-
private injector: IInjectorService;
42-
43-
private getRouterFromAngularJS(): UIRouterReact {
44-
if (this.injector) return this.injector.get('$uiRouter');
45-
}
46-
47-
private getParentViewFromAngularJS() {
48-
let ref = this.ref;
49-
let steps = parseInt(this.props.parentContextLevel);
50-
steps = isNaN(steps) ? 0 : steps;
51-
52-
while (ref && steps--) ref = ref.parentElement;
53-
const $uiView = ref && angular.element(ref).inheritedData('$uiView');
54-
return $uiView && new ParentUIViewAddressAdapter($uiView);
55-
}
56-
57-
public componentDidMount() {
58-
this.setState({
59-
parentUIViewAddress: this.getParentViewFromAngularJS(),
60-
});
61-
}
62-
63-
private refCallback = (ref: HTMLElement) => {
64-
if (ref && ref !== this.ref) {
65-
this.ref = ref;
66-
this.injector = angular.element(ref).injector();
67-
const router = this.getRouterFromAngularJS();
68-
const parentUIViewAddress = this.getParentViewFromAngularJS();
69-
this.setState({ ready: true, router, parentUIViewAddress });
70-
// console.log(ref);
25+
export function UIRouterContextComponent(props: {
26+
parentContextLevel: string;
27+
inherited: boolean;
28+
children: ReactNode;
29+
}) {
30+
const { parentContextLevel, inherited, children } = props;
31+
const [contextFromAngularJS, setContextFromAngularJS] = useState(initialState);
32+
const routerFromReactContext = useContext(UIRouterContext);
33+
const parentUIViewFromReactContext = useContext(UIViewContext);
34+
const domRef = useRef(null);
35+
36+
// Once we have a DOM node, get the AngularJS injector and walk up the DOM to find the AngularJS $uiView
37+
const refCallback = (el: HTMLElement) => {
38+
if (el && el !== domRef.current) {
39+
domRef.current = el;
40+
const injector = angular.element(el).injector();
41+
42+
// get router from angularjs
43+
const router = injector?.get('$uiRouter');
44+
45+
// get parent uiview from angularjs
46+
let steps = parseInt(parentContextLevel, 10);
47+
steps = isNaN(steps) ? 0 : steps;
48+
49+
while (el && steps--) el = el.parentElement;
50+
const $uiView = el && angular.element(el).inheritedData('$uiView');
51+
const addr = $uiView && new ParentUIViewAddressAdapter($uiView);
52+
53+
setContextFromAngularJS({ router, addr } as IContextFromAngularJS);
7154
}
7255
};
7356

74-
private renderChild(child: ReactElement<any>) {
75-
// console.log('renderChild()', child);
76-
const inherited = this.props.inherited;
77-
return (
78-
<UIRouterConsumer>
79-
{routerFromReactContext => (
80-
<UIRouterProvider value={inherited && routerFromReactContext || this.state.router}>
81-
<UIViewConsumer>
82-
{parentUIViewFromReactContext => (
83-
<UIViewProvider value={inherited && parentUIViewFromReactContext || this.state.parentUIViewAddress}>
84-
{child}
85-
</UIViewProvider>
86-
)}
87-
</UIViewConsumer>
88-
</UIRouterProvider>
89-
)}
90-
</UIRouterConsumer>
91-
);
57+
// render a div once to get a ref into the dom
58+
// Use the dom ref to access the AngularJS state
59+
if (!domRef.current) {
60+
return <div ref={refCallback} />;
9261
}
9362

94-
public render() {
95-
const { ready } = this.state;
96-
const { children } = this.props;
63+
// We know the AngularJS state. Now render the {children}
64+
const childrenCount = React.Children.count(children);
9765

98-
const childrenCount = React.Children.count(children);
99-
if (!ready) {
100-
return <div ref={this.refCallback} />;
101-
}
102-
103-
return this.renderChild(childrenCount === 1 ? React.Children.only(children) : <div>{children}</div>);
104-
}
66+
return (
67+
<UIRouterContext.Provider value={(inherited && routerFromReactContext) || contextFromAngularJS.router}>
68+
<UIViewContext.Provider value={(inherited && parentUIViewFromReactContext) || contextFromAngularJS.addr}>
69+
{childrenCount === 1 ? React.Children.only(children) : <div>{children}</div>}
70+
</UIViewContext.Provider>
71+
</UIRouterContext.Provider>
72+
);
10573
}
10674

75+
UIRouterContextComponent.defaultProps = {
76+
parentContextLevel: '0',
77+
inherited: true,
78+
} as IUIRouterContextComponentProps;
79+
10780
/**
10881
* Get the fqn and context from the parent angularjs ui-view.
10982
* Uses the ui-view element's data

src/react/UIRouterReactContextDecorator.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,5 @@ import { UIRouterContextComponent } from './UIRouterReactContext';
2727
* @param Component the react component to wrap
2828
*/
2929
export function UIRouterContext(Component: { new (...args: any[]): React.Component<any, any> }): any {
30-
return class extends React.Component<any, any> {
31-
public render() {
32-
return <UIRouterContextComponent>{React.createElement(Component, this.props)}</UIRouterContextComponent>;
33-
}
34-
};
30+
return props => <UIRouterContextComponent>{React.createElement(Component, props)}</UIRouterContextComponent>;
3531
}

0 commit comments

Comments
 (0)