Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: split makeMutable to web and native implementations #6307

Merged
merged 6 commits into from
Jul 22, 2024
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
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';
import { withStyleAnimation } from '../animation/styleAnimation';
import type { SharedValue } from '../commonTypes';
import { makeUIMutable } from '../mutables';
import { makeMutableUI } from '../mutables';
import { LayoutAnimationType } from './animationBuilder';
import { runOnUIImmediately } from '../threads';
import type {
Expand Down Expand Up @@ -73,7 +73,7 @@ function createLayoutAnimationManager(): {

let value = mutableValuesForTag.get(tag);
if (value === undefined) {
value = makeUIMutable(style.initialValues);
value = makeMutableUI(style.initialValues);
mutableValuesForTag.set(tag, value);
} else {
stopObservingProgress(tag, value);
Expand Down
153 changes: 85 additions & 68 deletions packages/react-native-reanimated/src/mutables.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,47 @@
'use strict';
import { shouldBeUseWeb } from './PlatformChecker';
import type { Mutable } from './commonTypes';
import { makeShareableCloneRecursive } from './shareables';
import { shareableMappingCache } from './shareableMappingCache';
import { makeShareableCloneRecursive } from './shareables';
import { executeOnUIRuntimeSync, runOnUI } from './threads';
import { valueSetter } from './valueSetter';

const SHOULD_BE_USE_WEB = shouldBeUseWeb();

type Listener<Value> = (newValue: Value) => void;

export function makeUIMutable<Value>(initial: Value): Mutable<Value> {
export function makeMutableUI<Value>(initial: Value): Mutable<Value> {
'worklet';

const listeners = new Map<number, Listener<Value>>();
let value = initial;

const self: Mutable<Value> = {
set value(newValue) {
valueSetter(self, newValue);
},
const mutable: Mutable<Value> = {
get value() {
return value;
},
set value(newValue) {
valueSetter(mutable, newValue);
},

/**
* _value prop should only be accessed by the valueSetter implementation
* which may make the decision about updating the mutable value depending
* on the provided new value. All other places should only attempt to modify
* the mutable by assigning to value prop directly.
*/
get _value(): Value {
return value;
},
set _value(newValue: Value) {
value = newValue;
listeners.forEach((listener) => {
listener(newValue);
});
},
get _value(): Value {
return value;
},

modify: (modifier, forceUpdate = true) => {
valueSetter(
self,
mutable,
modifier !== undefined ? modifier(value) : value,
forceUpdate
);
Expand All @@ -51,94 +52,110 @@ export function makeUIMutable<Value>(initial: Value): Mutable<Value> {
removeListener: (id: number) => {
listeners.delete(id);
},

_animation: null,
_isReanimatedSharedValue: true,
};
return self;
return mutable;
}

export function makeMutable<Value>(initial: Value): Mutable<Value> {
let value: Value = initial;
function makeMutableNative<Value>(initial: Value): Mutable<Value> {
const handle = makeShareableCloneRecursive({
__init: () => {
'worklet';
return makeUIMutable(initial);
return makeMutableUI(initial);
},
});
// listeners can only work on JS thread on Web and jest environments
const listeners = SHOULD_BE_USE_WEB
? new Map<number, Listener<Value>>()
: undefined;

const mutable: Mutable<Value> = {
set value(newValue) {
if (SHOULD_BE_USE_WEB) {
valueSetter(mutable, newValue);
} else {
runOnUI(() => {
mutable.value = newValue;
})();
}
},
get value(): Value {
if (SHOULD_BE_USE_WEB) {
return value;
}
const uiValueGetter = executeOnUIRuntimeSync((sv: Mutable<Value>) => {
return sv.value;
});
return uiValueGetter(mutable);
},
set _value(newValue: Value) {
if (!SHOULD_BE_USE_WEB) {
throw new Error(
'[Reanimated] Setting `_value` directly is only possible on the UI runtime. Perhaps you want to assign to `value` instead?'
);
}
value = newValue;
listeners!.forEach((listener) => {
listener(newValue);
});
set value(newValue) {
runOnUI(() => {
mutable.value = newValue;
})();
},

get _value(): Value {
if (SHOULD_BE_USE_WEB) {
return value;
}
throw new Error(
'[Reanimated] Reading from `_value` directly is only possible on the UI runtime. Perhaps you passed an Animated Style to a non-animated component?'
);
},
set _value(_newValue: Value) {
throw new Error(
'[Reanimated] Setting `_value` directly is only possible on the UI runtime. Perhaps you want to assign to `value` instead?'
);
},

modify: (modifier, forceUpdate = true) => {
runOnUI(() => {
mutable.modify(modifier, forceUpdate);
})();
},
addListener: () => {
throw new Error(
'[Reanimated] Adding listeners is only possible on the UI runtime.'
);
},
removeListener: () => {
throw new Error(
'[Reanimated] Removing listeners is only possible on the UI runtime.'
);
},

_isReanimatedSharedValue: true,
};

shareableMappingCache.set(mutable, handle);
return mutable;
}

function makeMutableWeb<Value>(initial: Value): Mutable<Value> {
let value: Value = initial;
const listeners = new Map<number, Listener<Value>>();

const mutable: Mutable<Value> = {
get value(): Value {
return value;
},
set value(newValue) {
valueSetter(mutable, newValue);
},

get _value(): Value {
return value;
},
set _value(newValue: Value) {
value = newValue;
listeners.forEach((listener) => {
listener(newValue);
});
},

modify: (modifier, forceUpdate = true) => {
if (!SHOULD_BE_USE_WEB) {
runOnUI(() => {
mutable.modify(modifier, forceUpdate);
})();
} else {
valueSetter(
mutable,
modifier !== undefined ? modifier(mutable.value) : mutable.value,
forceUpdate
);
}
valueSetter(
mutable,
modifier !== undefined ? modifier(mutable.value) : mutable.value,
forceUpdate
);
},
addListener: (id: number, listener: Listener<Value>) => {
if (!SHOULD_BE_USE_WEB) {
throw new Error(
'[Reanimated] Adding listeners is only possible on the UI runtime.'
);
}
listeners!.set(id, listener);
listeners.set(id, listener);
},
removeListener: (id: number) => {
if (!SHOULD_BE_USE_WEB) {
throw new Error(
'[Reanimated] Removing listeners is only possible on the UI runtime.'
);
}
listeners!.delete(id);
listeners.delete(id);
},

_isReanimatedSharedValue: true,
};
shareableMappingCache.set(mutable, handle);

return mutable;
}

export const makeMutable = SHOULD_BE_USE_WEB
? makeMutableWeb
: makeMutableNative;