diff --git a/public/app/plugins/datasource/influxdb/components/useShadowedState.test.ts b/public/app/plugins/datasource/influxdb/components/useShadowedState.test.ts index d934f957642..1e38b366e33 100644 --- a/public/app/plugins/datasource/influxdb/components/useShadowedState.test.ts +++ b/public/app/plugins/datasource/influxdb/components/useShadowedState.test.ts @@ -2,7 +2,7 @@ import { renderHook, act } from '@testing-library/react-hooks'; import { useShadowedState } from './useShadowedState'; describe('useShadowedState', () => { - it('should work correctly', () => { + it('should handle outside changes', () => { const { result, rerender } = renderHook(({ outsideVal }) => useShadowedState(outsideVal), { initialProps: { outsideVal: '42' }, }); @@ -24,4 +24,44 @@ describe('useShadowedState', () => { // and verify the now has the value from the outside expect(result.current[0]).toBe('71'); }); + it('should handle changs applied from inside to outside', () => { + // this is a test-case created because of a bug that was + // found with this component, it happens when: + // 1. the value is changed inside + // 2. the inside-value gets applied to the outside-component, + // so now the outside-value again matches the inside-value + // 3. the value changes again inside + // at this point the value should be correct. + const { result, rerender } = renderHook(({ outsideVal }) => useShadowedState(outsideVal), { + initialProps: { outsideVal: '1' }, + }); + + // first we verify it has the initial value + expect(result.current[0]).toBe('1'); + + // then we change it + act(() => { + result.current[1]('2'); + }); + + // and verify it has the changed value + expect(result.current[0]).toBe('2'); + + // then we change the value from the outside to the same + // value as the one inside (2) + // (this simulates the case when the inside value gets + // propageted to the outside component) + rerender({ outsideVal: '2' }); + + // and verify the the value is ok + expect(result.current[0]).toBe('2'); + + // and now change the inside-value again + act(() => { + result.current[1]('3'); + }); + + // and verify the the value is ok + expect(result.current[0]).toBe('3'); + }); }); diff --git a/public/app/plugins/datasource/influxdb/components/useShadowedState.ts b/public/app/plugins/datasource/influxdb/components/useShadowedState.ts index 7c3cb2892c3..bfe2f9e51fc 100644 --- a/public/app/plugins/datasource/influxdb/components/useShadowedState.ts +++ b/public/app/plugins/datasource/influxdb/components/useShadowedState.ts @@ -1,13 +1,15 @@ -import { useState, useEffect, useRef } from 'react'; +import { useState, useEffect } from 'react'; +import { usePrevious } from 'react-use'; export function useShadowedState(outsideVal: T): [T, (newVal: T) => void] { const [currentVal, setCurrentVal] = useState(outsideVal); - const prevOutsideVal = useRef(outsideVal); + const prevOutsideVal = usePrevious(outsideVal); useEffect(() => { - // if the value changes from the outside, we accept it - if (prevOutsideVal.current !== outsideVal && currentVal !== outsideVal) { - prevOutsideVal.current = outsideVal; + const isOutsideValChanged = prevOutsideVal !== outsideVal; + // if the value changes from the outside, we accept it into the state + // (we only set it if it is different from the current value) + if (isOutsideValChanged && currentVal !== outsideVal) { setCurrentVal(outsideVal); } }, [outsideVal, currentVal]);