[7.x] Clean up generic hooks, use react-use instead (#53822) (#53884)

* Clean up generic hooks, use react-use instead (#53822)

As we recently added react-use as a dependency, makes sense to clean up those generic hooks from Kibana repo.

Removed custom hooks from kibana_react and other places:
useObservable
useUnmount
useShallowCompareEffect

react-use should be used instead:
import useObservable from 'react-use/lib/useObservable'
# Conflicts:
#	src/plugins/kibana_react/public/index.ts
#	x-pack/legacy/plugins/apm/public/context/LicenseContext/index.tsx

* fix merge
This commit is contained in:
Anton Dosov 2020-01-03 12:11:51 +03:00 committed by GitHub
parent 6f6aacb61a
commit c3f1d973e8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 26 additions and 321 deletions

View file

@ -231,7 +231,7 @@
"react-resize-detector": "^4.2.0",
"react-router-dom": "^5.1.2",
"react-sizeme": "^2.3.6",
"react-use": "^13.10.2",
"react-use": "^13.13.0",
"reactcss": "1.2.3",
"redux": "4.0.0",
"redux-actions": "2.2.1",

View file

@ -20,10 +20,11 @@
import React, { useReducer, useEffect, useMemo } from 'react';
import { EuiForm, EuiAccordion, EuiSpacer, EuiFormRow } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import useUnmount from 'react-use/lib/useUnmount';
import { VisState } from 'ui/vis';
import { aggTypes, AggType, AggParam, AggConfig } from 'ui/agg_types/';
import { IndexPattern } from 'ui/index_patterns';
import { aggTypes, AggType, AggParam, AggConfig } from 'ui/agg_types/';
import { DefaultEditorAggSelect } from './agg_select';
import { DefaultEditorAggParam } from './agg_param';
@ -44,9 +45,6 @@ import {
} from './agg_params_state';
import { editorConfigProviders } from '../../config/editor_config_providers';
import { FixedParam, TimeIntervalParam, EditorParamConfig } from '../../config/types';
// TODO: Below import is temporary, use `react-use` lib instead.
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { useUnmount } from '../../../../../../../plugins/kibana_react/public/util/use_unmount';
import { AggGroupNames } from '../agg_groups';
import { OnAggParamsChange } from './agg_common_props';

View file

@ -22,9 +22,9 @@ import React from 'react';
import classNames from 'classnames';
import { Subscription } from 'rxjs';
import { filter } from 'rxjs/operators';
import useShallowCompareEffect from 'react-use/lib/useShallowCompareEffect';
import { EuiLoadingChart, EuiProgress } from '@elastic/eui';
import theme from '@elastic/eui/dist/eui_theme_light.json';
import { useShallowCompareEffect } from '../../kibana_react/public';
import { IExpressionLoaderParams, IInterpreterRenderHandlers, RenderError } from './types';
import { ExpressionAST } from '../common/types';
import { ExpressionLoader } from './loader';

View file

@ -25,4 +25,4 @@ export * from './overlays';
export * from './ui_settings';
export * from './field_icon';
export * from './table_list_view';
export { toMountPoint, useShallowCompareEffect } from './util';
export { toMountPoint } from './util';

View file

@ -24,10 +24,10 @@ import { useUiSetting$ } from './use_ui_setting';
import { createKibanaReactContext } from '../context';
import { KibanaServices } from '../context/types';
import { Subject } from 'rxjs';
import { useObservable } from '../util/use_observable';
import { coreMock } from '../../../../core/public/mocks';
import useObservable from 'react-use/lib/useObservable';
jest.mock('../util/use_observable');
jest.mock('react-use/lib/useObservable');
const useObservableSpy = (useObservable as any) as jest.SpyInstance;
useObservableSpy.mockImplementation((observable, def) => def);

View file

@ -18,8 +18,8 @@
*/
import { useCallback, useMemo } from 'react';
import useObservable from 'react-use/lib/useObservable';
import { useKibana } from '../context';
import { useObservable } from '../util/use_observable';
/**
* Returns the current UI-settings value.

View file

@ -17,7 +17,4 @@
* under the License.
*/
export * from './use_observable';
export * from './use_unmount';
export * from './react_mount';
export * from './use_shallow_compare_effect';

View file

@ -1,54 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { renderHook, act } from '@testing-library/react-hooks';
import { Subject } from 'rxjs';
import { useObservable } from './use_observable';
test('default initial value is undefined', () => {
const subject$ = new Subject();
const { result } = renderHook(() => useObservable(subject$));
expect(result.current).toBe(undefined);
});
test('can specify initial value', () => {
const subject$ = new Subject();
const { result } = renderHook(() => useObservable(subject$, 123));
expect(result.current).toBe(123);
});
test('returns the latest value of observables', () => {
const subject$ = new Subject();
const { result } = renderHook(() => useObservable(subject$, 123));
act(() => {
subject$.next(125);
});
expect(result.current).toBe(125);
act(() => {
subject$.next(300);
subject$.next(400);
});
expect(result.current).toBe(400);
});
xtest('subscribes to observable only once', () => {});

View file

@ -1,34 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { useLayoutEffect, useState } from 'react';
import { Observable } from 'rxjs';
export function useObservable<T>(observable$: Observable<T>): T | undefined;
export function useObservable<T>(observable$: Observable<T>, initialValue: T): T;
export function useObservable<T>(observable$: Observable<T>, initialValue?: T): T | undefined {
const [value, update] = useState<T | undefined>(initialValue);
useLayoutEffect(() => {
const s = observable$.subscribe(update);
return () => s.unsubscribe();
}, [observable$]);
return value;
}

View file

@ -1,86 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { renderHook } from '@testing-library/react-hooks';
import { useShallowCompareEffect } from './use_shallow_compare_effect';
describe('useShallowCompareEffect', () => {
test("doesn't run effect on shallow change", () => {
const callback = jest.fn();
let deps = [1, { a: 'b' }, true];
const { rerender } = renderHook(() => useShallowCompareEffect(callback, deps));
expect(callback).toHaveBeenCalledTimes(1);
callback.mockClear();
// no change
rerender();
expect(callback).toHaveBeenCalledTimes(0);
callback.mockClear();
// no-change (new object with same properties)
deps = [1, { a: 'b' }, true];
rerender();
expect(callback).toHaveBeenCalledTimes(0);
callback.mockClear();
// change (new primitive value)
deps = [2, { a: 'b' }, true];
rerender();
expect(callback).toHaveBeenCalledTimes(1);
callback.mockClear();
// no-change
rerender();
expect(callback).toHaveBeenCalledTimes(0);
callback.mockClear();
// change (new primitive value)
deps = [1, { a: 'b' }, false];
rerender();
expect(callback).toHaveBeenCalledTimes(1);
callback.mockClear();
// change (new properties on object)
deps = [1, { a: 'c' }, false];
rerender();
expect(callback).toHaveBeenCalledTimes(1);
callback.mockClear();
});
test('runs effect on deep change', () => {
const callback = jest.fn();
let deps = [1, { a: { b: 'c' } }, true];
const { rerender } = renderHook(() => useShallowCompareEffect(callback, deps));
expect(callback).toHaveBeenCalledTimes(1);
callback.mockClear();
// no change
rerender();
expect(callback).toHaveBeenCalledTimes(0);
callback.mockClear();
// change (new nested object )
deps = [1, { a: { b: 'c' } }, true];
rerender();
expect(callback).toHaveBeenCalledTimes(1);
callback.mockClear();
});
});

View file

@ -1,80 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React, { useEffect, useRef } from 'react';
/**
* Similar to https://github.com/kentcdodds/use-deep-compare-effect
* but uses shallow compare instead of deep
*/
export function useShallowCompareEffect(
callback: React.EffectCallback,
deps: React.DependencyList
) {
useEffect(callback, useShallowCompareMemoize(deps));
}
function useShallowCompareMemoize(deps: React.DependencyList) {
const ref = useRef<React.DependencyList | undefined>(undefined);
if (!ref.current || deps.some((dep, index) => !shallowEqual(dep, ref.current![index]))) {
ref.current = deps;
}
return ref.current;
}
// https://github.com/facebook/fbjs/blob/master/packages/fbjs/src/core/shallowEqual.js
function shallowEqual(objA: any, objB: any): boolean {
if (is(objA, objB)) {
return true;
}
if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
return false;
}
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);
if (keysA.length !== keysB.length) {
return false;
}
// Test for A's keys different from B.
for (let i = 0; i < keysA.length; i++) {
if (
!Object.prototype.hasOwnProperty.call(objB, keysA[i]) ||
!is(objA[keysA[i]], objB[keysA[i]])
) {
return false;
}
}
return true;
}
/**
* IE11 does not support Object.is
*/
function is(x: any, y: any): boolean {
if (x === y) {
return x !== 0 || y !== 0 || 1 / x === 1 / y;
} else {
return x !== x && y !== y;
}
}

View file

@ -1,24 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { useEffect } from 'react';
export function useUnmount(fn: () => void): void {
useEffect(() => fn, []);
}

View file

@ -7,7 +7,7 @@
import { useCallback, useMemo } from 'react';
import { npSetup } from 'ui/new_platform';
import { useObservable } from './use_observable';
import useObservable from 'react-use/lib/useObservable';
/**
* This hook behaves like a `useState` hook in that it provides a requested

View file

@ -1,21 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { useEffect, useState } from 'react';
import { Observable } from 'rxjs';
export function useObservable<T>(observable$: Observable<T>): T | undefined;
export function useObservable<T>(observable$: Observable<T>, initialValue: T): T;
export function useObservable<T>(observable$: Observable<T>, initialValue?: T): T | undefined {
const [value, update] = useState<T | undefined>(initialValue);
useEffect(() => {
const s = observable$.subscribe(update);
return () => s.unsubscribe();
}, [observable$]);
return value;
}

View file

@ -7,10 +7,7 @@
import classNames from 'classnames';
import React, { useRef, FC } from 'react';
import { TooltipValueFormatter } from '@elastic/charts';
// TODO: Below import is temporary, use `react-use` lib instead.
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { useObservable } from '../../../../../../../../src/plugins/kibana_react/public/util/use_observable';
import useObservable from 'react-use/lib/useObservable';
import { chartTooltip$, ChartTooltipValue } from './chart_tooltip_service';

View file

@ -5185,6 +5185,11 @@
dependencies:
tslib "^1.9.3"
"@xobotyi/scrollbar-width@1.5.0":
version "1.5.0"
resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.5.0.tgz#488210bff634548040dc22a72f62722a85b134e1"
integrity sha512-BK+HR1D00F2xh7n4+5en8/dMkG13uvIXLmEbsjtc1702b7+VwXkvlBDKoRPJMbkRN5hD7VqWa3nS9fNT8JG3CA==
"@xtuc/ieee754@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
@ -12940,6 +12945,11 @@ fast-safe-stringify@^2.0.7:
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743"
integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==
fast-shallow-equal@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/fast-shallow-equal/-/fast-shallow-equal-0.1.1.tgz#44d01324d7fd31e00a67bb02b9396e283d526c22"
integrity sha512-XVP6nhaXLYOH6JZCWBcNaeEer9GJ5/8cJWUP+OLmgwWgEkJp5Kpl/fdpJ01zl0mpLxrk7f5J3hIv+GmjTCi7Mg==
fast-stream-to-buffer@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fast-stream-to-buffer/-/fast-stream-to-buffer-1.0.0.tgz#793340cc753e7ec9c7fb6d57a53a0b911cb0f588"
@ -24283,12 +24293,14 @@ react-transition-group@^2.2.1:
prop-types "^15.6.2"
react-lifecycles-compat "^3.0.4"
react-use@^13.10.2:
version "13.10.2"
resolved "https://registry.yarnpkg.com/react-use/-/react-use-13.10.2.tgz#4250d258ca9068662943299c01794a136408c8e9"
integrity sha512-z3VFSiPHW6arViGVnajO7YKY5OD+Z9LWcImoJdYHkau23cLSoTctxM3XENLpGxjhJlHaYiQZ6pPgq7pwGTqSZA==
react-use@^13.13.0:
version "13.13.0"
resolved "https://registry.yarnpkg.com/react-use/-/react-use-13.13.0.tgz#5d133c4d4d8d3f21f6ccf4ccbe54fbcd6fdafb36"
integrity sha512-J3/h5wvL6vXmecAvEnninCC3DviLMRWcQrEnouTliwws1b376DQKEgIFuTXlF8c3SKpXBQJdDDm1RpluokW6ag==
dependencies:
"@xobotyi/scrollbar-width" "1.5.0"
copy-to-clipboard "^3.2.0"
fast-shallow-equal "^0.1.1"
nano-css "^5.2.1"
react-fast-compare "^2.0.4"
resize-observer-polyfill "^1.5.1"