diff --git a/src/web/hooks/__tests__/usePreviousValue.jsx b/src/web/hooks/__tests__/usePreviousValue.jsx new file mode 100644 index 0000000000..61b68e411e --- /dev/null +++ b/src/web/hooks/__tests__/usePreviousValue.jsx @@ -0,0 +1,41 @@ +/* SPDX-FileCopyrightText: 2024 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import {useState} from 'react'; + +import {describe, test, expect} from '@gsa/testing'; + +import {fireEvent, render, screen} from 'web/utils/testing'; + +import usePreviousValue from '../usePreviousValue'; + +const TestComponent = () => { + const [value, setValue] = useState(0); + const previousValue = usePreviousValue(value); + return ( + <> + + {value} + {'' + previousValue} + + ); +}; + +describe('usePreviousValue', () => { + test('should return the previous value', () => { + render(); + + const value = screen.getByTestId('value'); + const previousValue = screen.getByTestId('previousValue'); + + expect(value).toHaveTextContent('0'); + expect(previousValue).toHaveTextContent('undefined'); + + fireEvent.click(screen.getByRole('button')); + + expect(value).toHaveTextContent('1'); + expect(previousValue).toHaveTextContent('0'); + }); +}); diff --git a/src/web/hooks/usePreviousValue.js b/src/web/hooks/usePreviousValue.js new file mode 100644 index 0000000000..9215b0a61a --- /dev/null +++ b/src/web/hooks/usePreviousValue.js @@ -0,0 +1,35 @@ +/* SPDX-FileCopyrightText: 2024 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import {useEffect, useRef} from 'react'; + +/** + * React custom hook to store the value of the previous rendering + * + * It allows for checking if a value has changed since last render. + * + * Code Snippet + * + * ``` + * const [value, setValue] = useState(initialValue); + * const previousValue = usePrevious(value); + * + * if (value !== previousValue) { // value has changed + * doSomething() + * } + * ``` + */ +const usePreviousValue = value => { + const ref = useRef(); // initially the previous value is undefined + + useEffect(() => { + // will be called AFTER the calling component has rendered + ref.current = value; + }); + + return ref.current; +}; + +export default usePreviousValue;