Skip to content

Commit

Permalink
feat(UIView): add render prop API
Browse files Browse the repository at this point in the history
Add new `render` function prop API. This new API lets you control how
the routed component is rendered by the `UIView`:
```jsx
<UIView render={(Comp, props) => <Comp {…props} />} />
```

BREAKING CHANGE: Rename `Resolves` interface to `UIViewResolves` for
consistency.
BREAKING CHANGE: Rename `InjectedProps` interface to `UIViewResolves`
for consistency.

Closes #35
Closes #26
Closes #13
  • Loading branch information
elboman committed Mar 24, 2017
1 parent 15468a6 commit 1047b84
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 18 deletions.
5 changes: 0 additions & 5 deletions src/components/UIRouter.tsx
@@ -1,9 +1,4 @@
/**
* # Components
*
* React Components and their APIs
*
* @preferred
* @reactapi
* @module components
*/ /** */
Expand Down
30 changes: 23 additions & 7 deletions src/components/UIView.tsx
Expand Up @@ -4,6 +4,7 @@
*/ /** */
import * as React from 'react';
import {Component, PropTypes, ValidationMap, createElement, cloneElement, isValidElement} from 'react';
import {ReactElement, SFC, ClassType, StatelessComponent, ComponentClass, ClassicComponentClass} from 'react';
import {ActiveUIView, ViewContext, ViewConfig, Transition, ResolveContext, StateParams, applyPairs, extend} from "ui-router-core";
import {UIRouterReact} from "../index";
import {ReactViewConfig} from "../reactViews";
Expand All @@ -22,7 +23,7 @@ export interface UIViewAddress {
*
* This Typescript interface shows what fields are available on the `resolves` field.
*/
export interface Resolves {
export interface UIViewResolves {
/**
* Any key/value pair defined by a state's resolve
*
Expand All @@ -42,9 +43,17 @@ export interface Resolves {
$transition$: Transition
}

export interface InjectedProps {
/**
* Function type for [[UIViewProps.render]]
*
* If the `render` function prop is provided, the `UIView` will use it instead of rendering the component by itself.
* @internalapi
*/
export type RenderPropCallback = (Component: StatelessComponent<any> | ComponentClass<any> | ClassicComponentClass<any>, Props: any) => JSX.Element | null;

export interface UIViewInjectedProps {
transition?: Transition,
resolves?: Resolves,
resolves?: UIViewResolves,
className?:string,
style?: Object
}
Expand All @@ -54,13 +63,14 @@ export interface UIViewProps {
name?: string;
className?: string;
style?: Object;
render?: RenderPropCallback;
}

/** Component State for `UIView` */
export interface UIViewState {
id?: number;
loaded?: boolean;
component?: string;
component?: string | SFC<any> | ClassType<any,any,any> | ComponentClass<any>;
props?: any;
}

Expand Down Expand Up @@ -89,7 +99,8 @@ export class UIView extends Component<UIViewProps, UIViewState> {
static propTypes: ValidationMap<UIViewProps> = {
name: PropTypes.string,
className: PropTypes.string,
style: PropTypes.object
style: PropTypes.object,
render: PropTypes.func
}

static childContextTypes: ValidationMap<any> = {
Expand All @@ -102,7 +113,7 @@ export class UIView extends Component<UIViewProps, UIViewState> {
}

render() {
let { children } = this.props;
let { children, render } = this.props;
let { component, props, loaded } = this.state;
// register reference of child component
// register new hook right after component has been rendered
Expand All @@ -115,7 +126,12 @@ export class UIView extends Component<UIViewProps, UIViewState> {
let child = !loaded && isValidElement(children)
? cloneElement(children, props)
: createElement(component, props);
return child;

// if a render function is passed use that,
// otherwise render the component normally
return typeof render !== 'undefined' && loaded
? render(component, props)
: child;
}

getChildContext() {
Expand Down
18 changes: 18 additions & 0 deletions src/components/components.ts
@@ -0,0 +1,18 @@
/**
* # Components
*
* UI-Router React Components and their APIs:
* - [[UIRouter]]: Main router component
* - [[UIView]]: A viewport for routed components
* - [[UISref]]: A state ref to a target state; navigates when clicked
* - [[UISrefActive]]: Adds a css class when a UISref's target state (or a child state) is active
*
* @preferred
* @reactapi
* @module components
*/ /** */

export * from "./UIRouter";
export * from "./UIView";
export * from "./UISref";
export * from "./UISrefActive";
5 changes: 1 addition & 4 deletions src/index.ts
Expand Up @@ -12,10 +12,7 @@
export {trace} from "ui-router-core";
export {ReactStateDeclaration} from "./interface";

export {UIView, InjectedProps, Resolves} from "./components/UIView";
export {UISref} from "./components/UISref";
export {UISrefActive} from "./components/UISrefActive";
export {UIRouter} from "./components/UIRouter";
export * from './components/components';

export {UIRouterReact} from "./core";

Expand Down
4 changes: 2 additions & 2 deletions src/interface.tsx
Expand Up @@ -2,7 +2,7 @@
* @reactapi
* @module react
*/ /** */
import {Component} from "react";
import {Component, ReactElement, StatelessComponent, ComponentClass, ClassicComponentClass} from "react";
import {StateDeclaration, _ViewDeclaration} from "ui-router-core";
import {ParamDeclaration} from "ui-router-core";
import {IInjectable} from "ui-router-core";
Expand Down Expand Up @@ -177,7 +177,7 @@ export interface ReactViewDeclaration extends _ViewDeclaration {
* }
* ```
*/
component?: any;
component?: StatelessComponent<any> | ComponentClass<any> | ClassicComponentClass<any>;

/**
* @hidden
Expand Down

0 comments on commit 1047b84

Please sign in to comment.