Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/useLiveRef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import {useRef} from 'react';
/**
* Creates a mutable reference to a value, useful when you need to
* maintain a reference to a value that may change over time without triggering re-renders.
*
* @deprecated This hook breaks the Rules of React, and should not be used.
* The migration effort to remove it safely is not currently planned.
*/
function useLiveRef<T>(value: T) {
const ref = useRef<T>(value);
Expand Down
13 changes: 7 additions & 6 deletions lib/useOnyx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,13 @@ function useOnyx<TKey extends OnyxKey, TReturnValue = OnyxValue<TKey>>(
const previousKey = usePrevious(key);

const currentDependenciesRef = useLiveRef(dependencies);
const currentSelectorRef = useLiveRef(options?.selector);
const selector = options?.selector;

// Create memoized version of selector for performance
const memoizedSelector = useMemo(() => {
if (!options?.selector) return null;
if (!selector) {
return null;
}

let lastInput: OnyxValue<TKey> | undefined;
let lastOutput: TReturnValue;
Expand All @@ -91,14 +93,13 @@ function useOnyx<TKey extends OnyxKey, TReturnValue = OnyxValue<TKey>>(

return (input: OnyxValue<TKey> | undefined): TReturnValue => {
const currentDependencies = currentDependenciesRef.current;
const currentSelector = currentSelectorRef.current;

// Recompute if input changed, dependencies changed, or first time
const dependenciesChanged = !shallowEqual(lastDependencies, currentDependencies);
if (!hasComputed || lastInput !== input || dependenciesChanged) {
// Only proceed if we have a valid selector
if (currentSelector) {
const newOutput = currentSelector(input);
if (selector) {
const newOutput = selector(input);

// Deep equality mode: only update if output actually changed
if (!hasComputed || !deepEqual(lastOutput, newOutput) || dependenciesChanged) {
Expand All @@ -112,7 +113,7 @@ function useOnyx<TKey extends OnyxKey, TReturnValue = OnyxValue<TKey>>(

return lastOutput;
};
}, [currentDependenciesRef, currentSelectorRef, options?.selector]);
}, [currentDependenciesRef, selector]);

// Stores the previous cached value as it's necessary to compare with the new value in `getSnapshot()`.
// We initialize it to `null` to simulate that we don't have any value from cache yet.
Expand Down
1 change: 0 additions & 1 deletion tests/unit/useOnyxTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,6 @@ describe('useOnyx', () => {
expect(result.current[1].status).toEqual('loaded');

selector = (entry: OnyxEntry<{id: string; name: string}>) => `id - ${entry?.id}, name - ${entry?.name} - selector changed`;
// In a react app we expect the selector ref to change during a rerender (see selectorRef/useLiveRef)
rerender(undefined);

expect(result.current[0]).toEqual('id - test_id, name - test_name - selector changed');
Expand Down