Skip to content

Commit

Permalink
fix(react): React 16.3+ compatibility using `getDerivedPropsFromState…
Browse files Browse the repository at this point in the history
…(props, state)`
  • Loading branch information
STRML committed Aug 26, 2019
1 parent c5adeac commit fea778c
Showing 1 changed file with 48 additions and 31 deletions.
79 changes: 48 additions & 31 deletions lib/Draggable.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ type DraggableState = {
dragged: boolean,
x: number, y: number,
slackX: number, slackY: number,
isElementSVG: boolean
isElementSVG: boolean,
prevPropsPosition: ?ControlPosition,
};

export type DraggableProps = {
Expand Down Expand Up @@ -172,6 +173,26 @@ export default class Draggable extends React.Component<DraggableProps, Draggable
scale: 1
};

// React 16.3+
// Arity (props, state)
static getDerivedStateFromProps({position}: DraggableProps, {prevPropsPosition}: DraggableState) {
// Set x/y if a new position is provided in props that is different than the previous.
if (
position &&
(!prevPropsPosition ||
position.x !== prevPropsPosition.x || position.y !== prevPropsPosition.y
)
) {
log('Draggable: getDerivedStateFromProps %j', {position, prevPropsPosition})
return {
x: position.x,
y: position.y,
prevPropsPosition: {...position}
};
}
return null;
}

constructor(props: DraggableProps) {
super(props);

Expand All @@ -186,6 +207,8 @@ export default class Draggable extends React.Component<DraggableProps, Draggable
x: props.position ? props.position.x : props.defaultPosition.x,
y: props.position ? props.position.y : props.defaultPosition.y,

prevPropsPosition: {...props.position},

// Used for compensating for out-of-bounds drags
slackX: 0, slackY: 0,

Expand All @@ -204,19 +227,7 @@ export default class Draggable extends React.Component<DraggableProps, Draggable
componentDidMount() {
// Check to see if the element passed is an instanceof SVGElement
if(typeof window.SVGElement !== 'undefined' && ReactDOM.findDOMNode(this) instanceof window.SVGElement) {
this.setState({ isElementSVG: true });
}
}

componentWillReceiveProps(nextProps: Object) {
// Set x/y if position has changed
if (nextProps.position &&
(!this.props.position ||
nextProps.position.x !== this.props.position.x ||
nextProps.position.y !== this.props.position.y
)
) {
this.setState({ x: nextProps.position.x, y: nextProps.position.y });
this.setState({isElementSVG: true});
}
}

Expand Down Expand Up @@ -308,44 +319,50 @@ export default class Draggable extends React.Component<DraggableProps, Draggable
};

render(): ReactElement<any> {
let style = {}, svgTransform = null;
const {
axis,
bounds,
children,
defaultPosition,
defaultClassName,
defaultClassNameDragging,
defaultClassNameDragged,
position,
positionOffset,
...draggableCoreProps
} = this.props;

let style = {};
let svgTransform = null;

// If this is controlled, we don't want to move it - unless it's dragging.
const controlled = Boolean(this.props.position);
const controlled = Boolean(position);
const draggable = !controlled || this.state.dragging;

const position = this.props.position || this.props.defaultPosition;
const validPosition = position || defaultPosition;
const transformOpts = {
// Set left if horizontal drag is enabled
x: canDragX(this) && draggable ?
this.state.x :
position.x,
validPosition.x,

// Set top if vertical drag is enabled
y: canDragY(this) && draggable ?
this.state.y :
position.y
validPosition.y
};

// If this element was SVG, we use the `transform` attribute.
if (this.state.isElementSVG) {
svgTransform = createSVGTransform(transformOpts, this.props.positionOffset);
svgTransform = createSVGTransform(transformOpts, positionOffset);
} else {
// Add a CSS transform to move the element around. This allows us to move the element around
// without worrying about whether or not it is relatively or absolutely positioned.
// If the item you are dragging already has a transform set, wrap it in a <span> so <Draggable>
// has a clean slate.
style = createCSSTransform(transformOpts, this.props.positionOffset);
style = createCSSTransform(transformOpts, positionOffset);
}

const {
defaultClassName,
defaultClassNameDragging,
defaultClassNameDragged
} = this.props;

const children = React.Children.only(this.props.children);

// Mark with class while dragging
const className = classNames((children.props.className || ''), defaultClassName, {
[defaultClassNameDragging]: this.state.dragging,
Expand All @@ -355,8 +372,8 @@ export default class Draggable extends React.Component<DraggableProps, Draggable
// Reuse the child provided
// This makes it flexible to use whatever element is wanted (div, ul, etc)
return (
<DraggableCore {...this.props} onStart={this.onDragStart} onDrag={this.onDrag} onStop={this.onDragStop}>
{React.cloneElement(children, {
<DraggableCore {...draggableCoreProps} onStart={this.onDragStart} onDrag={this.onDrag} onStop={this.onDragStop}>
{React.cloneElement(React.Children.only(children), {
className: className,
style: {...children.props.style, ...style},
transform: svgTransform
Expand Down

0 comments on commit fea778c

Please sign in to comment.