Skip to content

Commit cd8777a

Browse files
wallzerochristopherthielen
authored andcommitted
feat(package): fixes to support React.StrictMode (#512)
1 parent 0f4a233 commit cd8777a

File tree

13 files changed

+108
-75
lines changed

13 files changed

+108
-75
lines changed

examples/typescript/index.tsx

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,19 @@ const routerConfig = (router: UIRouterReact) => {
5555

5656
let el = document.getElementById('react-app');
5757
let app = (
58-
<UIRouter plugins={[pushStateLocationPlugin]} states={[home, child, nest]} config={routerConfig}>
59-
<div>
60-
<UISrefActive class="active">
61-
<UISref to="home">
62-
<a>Home</a>
63-
</UISref>
64-
</UISrefActive>
65-
<UIView render={(Comp, props) => <Comp {...props} foo="bar" />}>
66-
<p>Content will load here</p>
67-
</UIView>
68-
</div>
69-
</UIRouter>
58+
<React.StrictMode>
59+
<UIRouter plugins={[pushStateLocationPlugin]} states={[home, child, nest]} config={routerConfig}>
60+
<div>
61+
<UISrefActive class="active">
62+
<UISref to="home">
63+
<a>Home</a>
64+
</UISref>
65+
</UISrefActive>
66+
<UIView render={(Comp, props) => <Comp {...props} foo="bar" />}>
67+
<p>Content will load here</p>
68+
</UIView>
69+
</div>
70+
</UIRouter>
71+
</React.StrictMode>
7072
);
7173
ReactDOM.render(app, el);

examples/typescript/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@
1717
"tsconfig-paths-webpack-plugin": "^3.0.4",
1818
"typescript": "2.8.3",
1919
"webpack": "^4.7.0",
20-
"webpack-dev-server": "^3.1.4"
20+
"webpack-dev-server": "3.7.1"
2121
}
2222
}

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
"react": "^16.5.1",
7272
"react-dom": "^16.5.1",
7373
"react-test-renderer": "^16.5.1",
74+
"tsconfig-paths-webpack-plugin": "^3.2.0",
7475
"ts-jest": "^23.1.3",
7576
"ts-loader": "^6.0.2",
7677
"typescript": "^3.0.1",
@@ -96,7 +97,7 @@
9697
"testRegex": "/__tests__/.*\\.(ts|tsx|js)$",
9798
"globals": {
9899
"ts-jest": {
99-
"tsConfigFile": "../tsconfig.jest.json"
100+
"tsConfigFile": "./tsconfig.jest.json"
100101
}
101102
}
102103
},

src/components/UIRouter.tsx

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
* @module components
44
*/ /** */
55
import * as React from 'react';
6-
import { Component, Children } from 'react';
6+
import { Component } from 'react';
77
import * as PropTypes from 'prop-types';
88

9-
import { UIRouterPlugin, servicesPlugin } from '@uirouter/core';
9+
import { servicesPlugin } from '@uirouter/core';
1010

1111
import { UIRouterReact, ReactStateDeclaration } from '../index';
1212

@@ -44,21 +44,21 @@ export interface UIRouterState {
4444

4545
/** @hidden */
4646
export const InstanceOrPluginsMissingError = new Error(`Router instance or plugins missing.
47-
You must either provide a location plugin via the plugins prop:
48-
49-
<UIRouter plugins={[pushStateLocationPlugin]} states={[···]}>
50-
<UIView />
51-
</UIRouter>
52-
53-
or initialize the router yourself and pass the instance via props:
54-
55-
const router = new UIRouterReact();
56-
router.plugin(pushStateLocationPlugin);
57-
···
58-
<UIRouter router={router}>
59-
<UIView />
60-
</UIRouter>
61-
`);
47+
You must either provide a location plugin via the plugins prop:
48+
49+
<UIRouter plugins={[pushStateLocationPlugin]} states={[···]}>
50+
<UIView />
51+
</UIRouter>
52+
53+
or initialize the router yourself and pass the instance via props:
54+
55+
const router = new UIRouterReact();
56+
router.plugin(pushStateLocationPlugin);
57+
···
58+
<UIRouter router={router}>
59+
<UIView />
60+
</UIRouter>
61+
`);
6262

6363
/** @hidden */
6464
export const UIRouterInstanceUndefinedError = new Error(
@@ -76,24 +76,27 @@ export class UIRouter extends Component<UIRouterProps, UIRouterState> {
7676

7777
router: UIRouterReact;
7878

79-
constructor(props, context) {
80-
super(props, context);
81-
// check if a router instance is provided
82-
if (props.router) {
83-
this.router = props.router;
84-
} else if (props.plugins) {
85-
this.router = new UIRouterReact();
86-
this.router.plugin(servicesPlugin);
87-
props.plugins.forEach(plugin => this.router.plugin(plugin));
88-
if (props.config) props.config(this.router);
89-
(props.states || []).forEach(state => this.router.stateRegistry.register(state));
90-
} else {
91-
throw InstanceOrPluginsMissingError;
79+
componentDidMount() {
80+
if (!this.router) {
81+
// check if a router instance is provided
82+
if (this.props.router) {
83+
this.router = this.props.router;
84+
} else if (this.props.plugins) {
85+
this.router = new UIRouterReact();
86+
this.router.plugin(servicesPlugin);
87+
this.props.plugins.forEach(plugin => this.router.plugin(plugin));
88+
if (this.props.config) this.props.config(this.router);
89+
(this.props.states || []).forEach(state => this.router.stateRegistry.register(state));
90+
} else {
91+
throw InstanceOrPluginsMissingError;
92+
}
93+
94+
this.router.start();
95+
this.forceUpdate();
9296
}
93-
this.router.start();
9497
}
9598

9699
render() {
97-
return <UIRouterProvider value={this.router}>{this.props.children}</UIRouterProvider>;
100+
return this.router ? <UIRouterProvider value={this.router}>{this.props.children}</UIRouterProvider> : null;
98101
}
99102
}

src/components/UISref.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class Sref extends Component<SrefProps, any> {
4444
className: PropTypes.string,
4545
};
4646

47-
componentWillMount() {
47+
componentDidMount() {
4848
const addStateInfo = this.props.addStateInfoToParentActive;
4949
this.deregister = typeof addStateInfo === 'function' ? addStateInfo(this.props.to, this.props.params) : () => {};
5050
const router = this.props.router;
@@ -54,7 +54,9 @@ class Sref extends Component<SrefProps, any> {
5454
}
5555

5656
componentWillUnmount() {
57-
this.deregister();
57+
if (this.deregister) {
58+
this.deregister();
59+
}
5860
}
5961

6062
getOptions = () => {

src/components/UISrefActive.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
* @module components
44
*/ /** */
55
import * as React from 'react';
6-
import { Component, cloneElement, ValidationMap } from 'react';
6+
import { Component, cloneElement } from 'react';
77
import * as PropTypes from 'prop-types';
88
import * as _classNames from 'classnames';
99

10-
import { UIRouterReact, UISref, UIRouterConsumer } from '../index';
10+
import { UIRouterReact, UIRouterConsumer } from '../index';
1111
import { UIViewAddress } from './UIView';
1212
import { UIRouterInstanceUndefinedError } from './UIRouter';
1313

@@ -59,7 +59,7 @@ class SrefActive extends Component<UISrefActiveProps, any> {
5959
activeClasses: '',
6060
};
6161

62-
componentWillMount() {
62+
componentDidMount() {
6363
const router = this.props.router;
6464
if (typeof router === 'undefined') {
6565
throw UIRouterInstanceUndefinedError;
@@ -69,7 +69,9 @@ class SrefActive extends Component<UISrefActiveProps, any> {
6969
}
7070

7171
componentWillUnmount() {
72-
this.deregister();
72+
if (this.deregister) {
73+
this.deregister();
74+
}
7375
}
7476

7577
addStateInfo = (stateName, stateParams) => {

src/components/UIView.tsx

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ import {
88
ClassicComponentClass,
99
Component,
1010
ComponentClass,
11+
createContext,
1112
SFC,
13+
ReactNode,
1214
StatelessComponent,
1315
ValidationMap,
1416
Validator,
1517
cloneElement,
1618
createElement,
17-
isValidElement,
19+
isValidElement
1820
} from 'react';
1921
import * as PropTypes from 'prop-types';
2022

@@ -78,6 +80,7 @@ export interface UIViewInjectedProps {
7880

7981
/** Component Props for `UIView` */
8082
export interface UIViewProps {
83+
children?: ReactNode;
8184
router?: UIRouterReact;
8285
parentUIView?: UIViewAddress;
8386
name?: string;
@@ -100,7 +103,7 @@ export const TransitionPropCollisionError = new Error(
100103
);
101104

102105
/** @internalapi */
103-
export const { Provider: UIViewProvider, Consumer: UIViewConsumer } = React.createContext<UIViewAddress>(undefined);
106+
export const { Provider: UIViewProvider, Consumer: UIViewConsumer } = createContext<UIViewAddress>(undefined);
104107

105108
class View extends Component<UIViewProps, UIViewState> {
106109
// This object contains all the metadata for this UIView
@@ -168,7 +171,7 @@ class View extends Component<UIViewProps, UIViewState> {
168171
return <UIViewProvider value={this.uiViewAddress}>{ChildOrRenderFunction}</UIViewProvider>;
169172
}
170173

171-
componentWillMount() {
174+
componentDidMount() {
172175
const router = this.props.router;
173176
if (typeof router === 'undefined') {
174177
throw UIRouterInstanceUndefinedError;
@@ -199,7 +202,9 @@ class View extends Component<UIViewProps, UIViewState> {
199202
}
200203

201204
componentWillUnmount() {
202-
this.deregister();
205+
if (this.deregister) {
206+
this.deregister();
207+
}
203208
}
204209

205210
/**
@@ -262,9 +267,18 @@ class View extends Component<UIViewProps, UIViewState> {
262267
}
263268
}
264269

265-
export class UIView extends React.Component<UIViewProps, any> {
270+
View.propTypes = {
271+
router: PropTypes.object.isRequired as Validator<UIRouterReact>,
272+
parentUIView: PropTypes.object as Validator<UIViewAddress>,
273+
name: PropTypes.string,
274+
className: PropTypes.string,
275+
style: PropTypes.object,
276+
render: PropTypes.func,
277+
} as ValidationMap<UIViewProps>;
278+
279+
export class UIView extends Component<UIViewProps, any> {
266280
static displayName = 'UIView';
267-
static __internalViewComponent: React.ComponentClass<UIViewProps> = View;
281+
static __internalViewComponent: ComponentClass<UIViewProps> = View;
268282

269283
render() {
270284
return (

src/components/__tests__/UISref.test.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ describe('<UISref>', () => {
139139
);
140140
await router.stateService.go('state');
141141
wrapper.update();
142-
const stateServiceGoSpy = jest.spyOn(wrapper.instance().router.stateService, 'go');
142+
// @ts-ignore
143+
const stateServiceGoSpy = jest.spyOn(router.stateService, 'go');
143144
const link = wrapper.find('a');
144145
link.simulate('click');
145146
link.simulate('click', { button: 1 });
@@ -162,6 +163,7 @@ describe('<UISref>', () => {
162163
.find('Sref')
163164
.at(0);
164165
expect(uiSref.instance().context.parentUIViewAddress).toBeUndefined();
166+
// @ts-ignore
165167
expect(uiSref.instance().getOptions().relative.name).toBe('');
166168
});
167169
});

src/components/__tests__/UISrefActive.test.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ describe('<UISrefActive>', () => {
204204
.at(0)
205205
.instance();
206206
expect(instance.context.parentUIViewAddress).toBeUndefined();
207+
// @ts-ignore
207208
expect(instance.states[0].state.name).toBe('parent.child1');
208209
});
209210

@@ -232,6 +233,7 @@ describe('<UISrefActive>', () => {
232233
.at(0)
233234
.instance();
234235
expect(instance.context.parentUIViewAddress).toBeUndefined();
236+
// @ts-ignore
235237
expect(instance.states.length).toBe(3);
236238
});
237239

@@ -266,6 +268,7 @@ describe('<UISrefActive>', () => {
266268
.find('SrefActive')
267269
.at(0)
268270
.instance();
271+
// @ts-ignore
269272
expect(instance.states.length).toBe(3);
270273

271274
router.stateRegistry.register({
@@ -340,8 +343,10 @@ describe('<UISrefActive>', () => {
340343
.find('SrefActive')
341344
.at(0)
342345
.instance();
346+
// @ts-ignore
343347
expect(instance.states.length).toBe(1);
344348
wrapper.setProps({ show: false });
349+
// @ts-ignore
345350
expect(instance.states.length).toBe(0);
346351
});
347352

0 commit comments

Comments
 (0)