-
Notifications
You must be signed in to change notification settings - Fork 75
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat:
useMemoWithPrevious
React hook (#2820)
Resolves #2819 **Changes:** Introduces a new `useMemoWithPrevious` hook to manage previous and current values with reset capability, replacing the manual `useRef` implementation in ContainerLogModal. This hook provides a more robust way to track state changes while maintaining previous values. **Implementation Details:** - Created new `useMemoWithPrevious` hook that returns both current and previous values along with a reset function - Updated ContainerLogModal to use the new hook for tracking log line numbers - Replaced manual `previousLastLineNumber` ref management with the new hook's `resetPrevious` functionality - Improved state management when switching kernel IDs or changing log sizes **Testing Requirements:** - Verify log highlighting works correctly when switching between different kernels - Confirm previous line numbers are properly reset when changing log sizes - Check that log display maintains proper highlighting of new content
- Loading branch information
Showing
3 changed files
with
135 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import { useMemoWithPrevious } from './useMemoWithPrevious'; | ||
import { renderHook, act } from '@testing-library/react'; | ||
|
||
describe('useMemoWithPrevious Hook', () => { | ||
it('should initialize with initialPrev', () => { | ||
const factory = jest.fn(() => 'current value'); | ||
const { result } = renderHook(() => | ||
useMemoWithPrevious(factory, [], { | ||
initialPrev: 'initial previous value', | ||
}), | ||
); | ||
|
||
expect(result.current[0].current).toBe('current value'); | ||
expect(result.current[0].previous).toBe('initial previous value'); | ||
expect(factory).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('should update current and previous when dependencies change', () => { | ||
let dep = 1; | ||
const factory = jest.fn(() => `current value ${dep}`); | ||
|
||
const { result, rerender } = renderHook(() => | ||
useMemoWithPrevious(factory, [dep]), | ||
); | ||
|
||
// Initial render | ||
expect(result.current[0].current).toBe('current value 1'); | ||
expect(result.current[0].previous).toBeUndefined(); | ||
expect(factory).toHaveBeenCalledTimes(1); | ||
|
||
// Update dep and rerender | ||
// act(() => { | ||
dep = 2; | ||
rerender(); | ||
// }); | ||
|
||
// After dependency change | ||
expect(result.current[0].current).toBe('current value 2'); | ||
expect(result.current[0].previous).toBe('current value 1'); | ||
expect(factory).toHaveBeenCalledTimes(2); | ||
}); | ||
|
||
it('should reset previous when resetPrevious is called', () => { | ||
let dep = 1; | ||
const factory = jest.fn(() => `current value ${dep}`); | ||
|
||
const { result, rerender } = renderHook(() => | ||
useMemoWithPrevious(factory, [dep], { | ||
initialPrev: 'initial previous value', | ||
}), | ||
); | ||
|
||
// Initial render | ||
expect(result.current[0].current).toBe('current value 1'); | ||
expect(result.current[0].previous).toBe('initial previous value'); | ||
|
||
// Update dep and rerender | ||
dep = 2; | ||
rerender(); | ||
|
||
expect(result.current[0].current).toBe('current value 2'); | ||
expect(result.current[0].previous).toBe('current value 1'); | ||
|
||
// Call resetPrevious | ||
act(() => { | ||
result.current[1].resetPrevious(); | ||
}); | ||
|
||
expect(result.current[0].current).toBe('current value 2'); | ||
expect(result.current[0].previous).toBe('initial previous value'); | ||
expect(factory).toHaveBeenCalledTimes(2); | ||
}); | ||
|
||
it('should not update previous if dependencies do not change', () => { | ||
const factory = jest.fn(() => 'current value'); | ||
const { result, rerender } = renderHook(() => | ||
useMemoWithPrevious(factory, []), | ||
); | ||
|
||
// Initial render | ||
expect(result.current[0].current).toBe('current value'); | ||
expect(result.current[0].previous).toBeUndefined(); | ||
|
||
// Rerender without changing dependencies | ||
rerender(); | ||
|
||
expect(result.current[0].current).toBe('current value'); | ||
expect(result.current[0].previous).toBeUndefined(); | ||
expect(factory).toHaveBeenCalledTimes(1); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { DependencyList, useEffect, useMemo, useRef, useState } from 'react'; | ||
|
||
export const useMemoWithPrevious = <T,>( | ||
factory: () => T, | ||
deps: DependencyList, | ||
{ initialPrev }: { initialPrev?: T } | undefined = {}, | ||
) => { | ||
const prevRef = useRef(initialPrev); | ||
const [prevResetKey, setPrevResetKey] = useState({}); | ||
|
||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
const current = useMemo(factory, deps); | ||
const memoizedPrev = useMemo(() => { | ||
return prevRef.current; | ||
// Only update when the reset key changes and deps change | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, [...deps, prevResetKey]); | ||
|
||
useEffect(() => { | ||
prevRef.current = current; | ||
// Only update when deps change | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, deps); | ||
|
||
return [ | ||
{ | ||
previous: memoizedPrev, | ||
current: current, | ||
}, | ||
{ | ||
resetPrevious: () => { | ||
prevRef.current = initialPrev; | ||
setPrevResetKey({}); | ||
}, | ||
}, | ||
] as const; | ||
}; |