Skip to content

Commit

Permalink
refactor: remove elevation handling from Portal implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
satya164 committed May 25, 2018
1 parent 14e92ba commit 430b3e6
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 74 deletions.
10 changes: 3 additions & 7 deletions src/components/Portal/Portal.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@ import * as React from 'react';
import PortalConsumer from './PortalConsumer';
import { PortalContext } from './PortalHost';

export type PortalProps = {
/**
* Elevation of the element in the z-axis
*/
elevation?: number,
type Props = {
/**
* Content of the `Portal`.
*/
Expand All @@ -19,10 +15,10 @@ export type PortalProps = {
/**
* Portal allows to render a component at a different place in the parent tree.
*/
export default function Portal(props: PortalProps) {
export default function Portal({ children }: Props) {
return (
<PortalContext.Consumer>
{manager => <PortalConsumer manager={manager} props={props} />}
{manager => <PortalConsumer manager={manager}>{children}</PortalConsumer>}
</PortalContext.Consumer>
);
}
7 changes: 3 additions & 4 deletions src/components/Portal/PortalConsumer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,19 @@

import * as React from 'react';
import type { PortalMethods } from './PortalHost';
import type { PortalProps } from './Portal';

type Props = {
manager: PortalMethods,
props: PortalProps,
children: React.Node,
};

export default class PortalConsumer extends React.Component<Props> {
componentDidMount() {
this._key = this.props.manager.mount(this.props.props);
this._key = this.props.manager.mount(this.props.children);
}

componentDidUpdate() {
this.props.manager.update(this._key, this.props.props);
this.props.manager.update(this._key, this.props.children);
}

componentWillUnmount() {
Expand Down
25 changes: 12 additions & 13 deletions src/components/Portal/PortalHost.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,20 @@ import * as React from 'react';
import { View, StyleSheet } from 'react-native';
import PortalManager from './PortalManager';
import createReactContext, { type Context } from 'create-react-context';
import type { PortalProps } from './Portal';

type Props = {
children: React.Node,
style?: any,
};

type Operation =
| { type: 'mount', key: number, props: PortalProps }
| { type: 'update', key: number, props: PortalProps }
| { type: 'mount', key: number, children: React.Node }
| { type: 'update', key: number, children: React.Node }
| { type: 'unmount', key: number };

export type PortalMethods = {
mount: (props: PortalProps) => number,
update: (key: number, props: PortalProps) => void,
mount: (children: React.Node) => number,
update: (key: number, children: React.Node) => void,
unmount: (key: number) => void,
};

Expand All @@ -40,10 +39,10 @@ export default class PortalHost extends React.Component<Props> {
// eslint-disable-next-line default-case
switch (action.type) {
case 'mount':
manager.mount(action.key, action.props);
manager.mount(action.key, action.children);
break;
case 'update':
manager.update(action.key, action.props);
manager.update(action.key, action.children);
break;
case 'unmount':
manager.unmount(action.key);
Expand All @@ -52,23 +51,23 @@ export default class PortalHost extends React.Component<Props> {
}
}

_mount = (props: PortalProps) => {
_mount = (children: React.Node) => {
const key = this._nextKey++;

if (this._manager) {
this._manager.mount(key, props);
this._manager.mount(key, children);
} else {
this._queue.push({ type: 'mount', key, props });
this._queue.push({ type: 'mount', key, children });
}

return key;
};

_update = (key: number, props: PortalProps) => {
_update = (key: number, children: React.Node) => {
if (this._manager) {
this._manager.update(key, props);
this._manager.update(key, children);
} else {
const op = { type: 'mount', key, props };
const op = { type: 'mount', key, children };
const index = this._queue.findIndex(
o => o.type === 'mount' || (o.type === 'update' && o.key === key)
);
Expand Down
60 changes: 10 additions & 50 deletions src/components/Portal/PortalManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,14 @@

import * as React from 'react';
import { View, StyleSheet } from 'react-native';
import type { PortalProps } from './Portal';

type State = {
portals: Array<{
key: number,
props: PortalProps,
children: React.Node,
}>,
};

type Group = {
key: number,
elevation?: number,
items: React.Node[],
};

/**
* Portal host is the component which actually renders all Portals.
*/
Expand All @@ -25,17 +18,17 @@ export default class PortalManager extends React.PureComponent<{}, State> {
portals: [],
};

mount = (key: number, props: PortalProps) => {
mount = (key: number, children: React.Node) => {
this.setState(state => ({
portals: [...state.portals, { key, props }],
portals: [...state.portals, { key, children }],
}));
};

update = (key: number, props: PortalProps) =>
update = (key: number, children: React.Node) =>
this.setState(state => ({
portals: state.portals.map(item => {
if (item.key === key) {
return { ...item, props };
return { ...item, children };
}
return item;
}),
Expand All @@ -47,43 +40,10 @@ export default class PortalManager extends React.PureComponent<{}, State> {
}));

render() {
return this.state.portals
.reduce((acc: Group[], curr) => {
const { elevation, children } = curr.props;

let group: ?Group = acc.find(it => it.elevation === elevation);

if (group && typeof elevation === 'number') {
group = {
key: elevation,
elevation,
items: [
...group.items,
React.cloneElement(children, { key: curr.key }),
],
};
return acc.map(g => {
if (group && g.elevation === elevation) {
return group;
}
return g;
});
}
group = {
key: typeof elevation === 'undefined' ? curr.key : elevation,
elevation,
items: [React.cloneElement(children, { key: curr.key })],
};
return [...acc, group];
}, [])
.map(({ key, items }) => (
<View
key={key}
pointerEvents="box-none"
style={StyleSheet.absoluteFill}
>
{items}
</View>
));
return this.state.portals.map(({ key, children }) => (
<View key={key} pointerEvents="box-none" style={StyleSheet.absoluteFill}>
{children}
</View>
));
}
}

0 comments on commit 430b3e6

Please sign in to comment.