Skip to content

Commit

Permalink
fix: lifecycle methods in MultiSlider, Resizable, and TableBodyCell (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
adidahiya authored Oct 9, 2019
1 parent 58143d8 commit 69882d3
Show file tree
Hide file tree
Showing 10 changed files with 260 additions and 229 deletions.
7 changes: 7 additions & 0 deletions packages/core/src/common/abstractComponent2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ import { isNodeEnv } from "./utils";
* in order to add some common functionality like runtime props validation.
*/
export abstract class AbstractComponent2<P, S = {}, SS = {}> extends React.Component<P, S, SS> {
// unsafe lifecycle methods
public componentWillUpdate: never;
public componentWillReceiveProps: never;
public componentWillMount: never;
// this should be static, not an instance method
public getDerivedStateFromProps: never;

/** Component displayName should be `public static`. This property exists to prevent incorrect usage. */
protected displayName: never;

Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/common/abstractPureComponent2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ import { isNodeEnv } from "./utils";
*/
export abstract class AbstractPureComponent2<P, S = {}, SS = {}> extends React.PureComponent<P, S, SS> {
// unsafe lifecycle method
public componentWillUpdate: never;
public componentWillReceiveProps: never;
public componentWillMount: never;
// this should be static, not an instance method
public getDerivedStateFromProps: never;

/** Component displayName should be `public static`. This property exists to prevent incorrect usage. */
protected displayName: never;
Expand Down
65 changes: 34 additions & 31 deletions packages/core/src/components/slider/multiSlider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,6 @@ import { argMin, fillValues, formatPercentage } from "./sliderUtils";
const MultiSliderHandle: React.SFC<IHandleProps> = () => null;
MultiSliderHandle.displayName = `${DISPLAYNAME_PREFIX}.MultiSliderHandle`;

// TODO: move this to props.ts in a follow up PR
/** A convenience type for React's optional children prop. */
export interface IChildrenProps {
children?: React.ReactNode;
}

export interface ISliderBaseProps extends IProps {
/**
* Whether the slider is non-interactive.
Expand Down Expand Up @@ -140,7 +134,7 @@ export class MultiSlider extends AbstractPureComponent2<IMultiSliderProps, ISlid

public static Handle = MultiSliderHandle;

public static getDerivedStateFromProps(props: IMultiSliderProps & IChildrenProps) {
public static getDerivedStateFromProps(props: IMultiSliderProps) {
return { labelPrecision: MultiSlider.getLabelPrecision(props) };
}

Expand All @@ -156,10 +150,18 @@ export class MultiSlider extends AbstractPureComponent2<IMultiSliderProps, ISlid
};

private handleElements: Handle[] = [];
private handleProps: IHandleProps[];

private trackElement: HTMLElement | null;

public getSnapshotBeforeUpdate(prevProps: IMultiSliderProps): null {
const prevHandleProps = getSortedInteractiveHandleProps(prevProps);
const newHandleProps = getSortedInteractiveHandleProps(this.props);
if (newHandleProps.length !== prevHandleProps.length) {
// clear refs
this.handleElements = [];
}
return null;
}

public render() {
const classes = classNames(
Classes.SLIDER,
Expand All @@ -182,26 +184,15 @@ export class MultiSlider extends AbstractPureComponent2<IMultiSliderProps, ISlid
}

public componentDidMount() {
this.handleProps = getSortedInteractiveHandleProps(this.props);
this.updateTickSize();
}

public getSnapshotBeforeUpdate(): {} | null {
const newHandleProps = getSortedInteractiveHandleProps(this.props);
if (newHandleProps.length !== this.handleProps.length) {
this.handleElements = [];
}
this.handleProps = newHandleProps;

return null;
}

public componentDidUpdate(prevProps: IMultiSliderProps, prevState: ISliderState, ss: {}) {
super.componentDidUpdate(prevProps, prevState, ss);
this.updateTickSize();
}

protected validateProps(props: IMultiSliderProps & IChildrenProps) {
protected validateProps(props: React.PropsWithChildren<IMultiSliderProps>) {
if (props.stepSize <= 0) {
throw new Error(Errors.SLIDER_ZERO_STEP);
}
Expand Down Expand Up @@ -290,17 +281,20 @@ export class MultiSlider extends AbstractPureComponent2<IMultiSliderProps, ISlid

private renderHandles() {
const { disabled, max, min, stepSize, vertical } = this.props;
if (!this.handleProps) {
const handleProps = getSortedInteractiveHandleProps(this.props);

if (handleProps.length === 0) {
return null;
}
return this.handleProps.map(({ value, type }, index) => (

return handleProps.map(({ value, type }, index) => (
<Handle
className={classNames({
[Classes.START]: type === HandleType.START,
[Classes.END]: type === HandleType.END,
})}
disabled={disabled}
key={`${index}-${this.handleProps.length}`}
key={`${index}-${handleProps.length}`}
label={this.formatLabel(value)}
max={max}
min={min}
Expand Down Expand Up @@ -366,7 +360,8 @@ export class MultiSlider extends AbstractPureComponent2<IMultiSliderProps, ISlid
};

private getNewHandleValues(newValue: number, oldIndex: number) {
const oldValues = this.handleProps.map(handle => handle.value);
const handleProps = getSortedInteractiveHandleProps(this.props);
const oldValues = handleProps.map(handle => handle.value);
const newValues = oldValues.slice();
newValues[oldIndex] = newValue;
newValues.sort((left, right) => left - right);
Expand All @@ -387,19 +382,23 @@ export class MultiSlider extends AbstractPureComponent2<IMultiSliderProps, ISlid

private findFirstLockedHandleIndex(startIndex: number, endIndex: number): number {
const inc = startIndex < endIndex ? 1 : -1;
const handleProps = getSortedInteractiveHandleProps(this.props);

for (let index = startIndex + inc; index !== endIndex + inc; index += inc) {
if (this.handleProps[index].interactionKind !== HandleInteractionKind.PUSH) {
if (handleProps[index].interactionKind !== HandleInteractionKind.PUSH) {
return index;
}
}

return -1;
}

private handleChange = (newValues: number[]) => {
const oldValues = this.handleProps.map(handle => handle.value);
const handleProps = getSortedInteractiveHandleProps(this.props);
const oldValues = handleProps.map(handle => handle.value);
if (!Utils.arraysEqual(newValues, oldValues)) {
Utils.safeInvoke(this.props.onChange, newValues);
this.handleProps.forEach((handle, index) => {
handleProps.forEach((handle, index) => {
if (oldValues[index] !== newValues[index]) {
Utils.safeInvoke(handle.onChange, newValues[index]);
}
Expand All @@ -408,8 +407,9 @@ export class MultiSlider extends AbstractPureComponent2<IMultiSliderProps, ISlid
};

private handleRelease = (newValues: number[]) => {
const handleProps = getSortedInteractiveHandleProps(this.props);
Utils.safeInvoke(this.props.onRelease, newValues);
this.handleProps.forEach((handle, index) => {
handleProps.forEach((handle, index) => {
Utils.safeInvoke(handle.onRelease, newValues[index]);
});
};
Expand Down Expand Up @@ -445,11 +445,14 @@ function getLabelPrecision({ labelPrecision, stepSize }: IMultiSliderProps) {
return labelPrecision == null ? Utils.countDecimalPlaces(stepSize) : labelPrecision;
}

function getSortedInteractiveHandleProps(props: IChildrenProps): IHandleProps[] {
function getSortedInteractiveHandleProps(props: React.PropsWithChildren<IMultiSliderProps>): IHandleProps[] {
return getSortedHandleProps(props, childProps => childProps.interactionKind !== HandleInteractionKind.NONE);
}

function getSortedHandleProps({ children }: IChildrenProps, predicate: (props: IHandleProps) => boolean = () => true) {
function getSortedHandleProps(
{ children }: React.PropsWithChildren<IMultiSliderProps>,
predicate: (props: IHandleProps) => boolean = () => true,
) {
const maybeHandles = React.Children.map(children, child =>
Utils.isElementOfType(child, MultiSlider.Handle) && predicate(child.props) ? child.props : null,
);
Expand Down
3 changes: 3 additions & 0 deletions packages/table/src/headers/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,9 @@ export class Header extends React.Component<IInternalHeaderProps, IHeaderState>
const { getIndexClass, selectedRegions } = this.props;

const cell = this.props.headerCellRenderer(index);
if (cell == null) {
return null;
}

const isLoading = cell.props.loading != null ? cell.props.loading : this.props.loading;
const isSelected = this.props.isCellSelected(index);
Expand Down
2 changes: 2 additions & 0 deletions packages/table/src/interactions/resizable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ export class Resizable extends AbstractPureComponent2<IResizableProps, IResizeab
return null;
}

public state: IResizeableState = Resizable.getDerivedStateFromProps(this.props, null);

public componentDidUpdate(prevProps: IResizableProps) {
if (prevProps.size !== this.props.size) {
this.setState(Resizable.getDerivedStateFromProps(this.props, null));
Expand Down
Loading

1 comment on commit 69882d3

@blueprint-bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix: lifecycle methods in MultiSlider, Resizable, and TableBodyCell (#3771)

Previews: documentation | landing | table

Please sign in to comment.