react-native-reanimated
Version:
More powerful alternative to Animated library for React Native.
205 lines (200 loc) • 6.11 kB
JavaScript
import { ReanimatedError } from "./errors.js";
import { logger } from "./logger/index.js";
import { shouldBeUseWeb } from "./PlatformChecker.js";
import { isFirstReactRender, isReactRendering } from "./reactUtils.js";
import { shareableMappingCache } from "./shareableMappingCache.js";
import { makeShareableCloneRecursive } from "./shareables.js";
import { executeOnUIRuntimeSync, runOnUI } from "./threads.js";
import { valueSetter } from "./valueSetter.js";
const SHOULD_BE_USE_WEB = shouldBeUseWeb();
function shouldWarnAboutAccessDuringRender() {
return __DEV__ && isReactRendering() && !isFirstReactRender();
}
function checkInvalidReadDuringRender() {
if (shouldWarnAboutAccessDuringRender()) {
logger.warn("Reading from `value` during component render. Please ensure that you don't access the `value` property nor use `get` method of a shared value while React is rendering a component.", {
strict: true
});
}
}
function checkInvalidWriteDuringRender() {
if (shouldWarnAboutAccessDuringRender()) {
logger.warn("Writing to `value` during component render. Please ensure that you don't access the `value` property nor use `set` method of a shared value while React is rendering a component.", {
strict: true
});
}
}
/**
* Adds `get` and `set` methods to the mutable object to handle access to
* `value` property.
*
* React Compiler disallows modifying return values of hooks. Even though
* assignment to `value` is a setter invocation, Compiler's static analysis
* doesn't detect it. That's why we provide a second API for users using the
* Compiler.
*/
function addCompilerSafeGetAndSet(mutable) {
'worklet';
Object.defineProperties(mutable, {
get: {
value() {
return mutable.value;
},
configurable: false,
enumerable: false
},
set: {
value(newValue) {
if (typeof newValue === 'function' &&
// If we have an animation definition, we don't want to call it here.
!newValue.__isAnimationDefinition) {
mutable.value = newValue(mutable.value);
} else {
mutable.value = newValue;
}
},
configurable: false,
enumerable: false
}
});
}
/**
* Hides the internal `_value` property of a mutable. It won't be visible to:
*
* - `Object.keys`,
* - `const prop in obj`,
* - Etc.
*
* This way when the user accidentally sends the SharedValue to React, he won't
* get an obscure error message.
*
* We hide for both _React runtime_ and _Worklet runtime_ mutables for
* uniformity of behavior.
*/
function hideInternalValueProp(mutable) {
'worklet';
Object.defineProperty(mutable, '_value', {
configurable: false,
enumerable: false
});
}
export function makeMutableUI(initial) {
'worklet';
const listeners = new Map();
let value = initial;
const mutable = {
get value() {
return value;
},
set value(newValue) {
valueSetter(mutable, newValue);
},
get _value() {
return value;
},
set _value(newValue) {
value = newValue;
listeners.forEach(listener => {
listener(newValue);
});
},
modify: (modifier, forceUpdate = true) => {
valueSetter(mutable, modifier !== undefined ? modifier(value) : value, forceUpdate);
},
addListener: (id, listener) => {
listeners.set(id, listener);
},
removeListener: id => {
listeners.delete(id);
},
_animation: null,
_isReanimatedSharedValue: true
};
hideInternalValueProp(mutable);
addCompilerSafeGetAndSet(mutable);
return mutable;
}
function makeMutableNative(initial) {
const handle = makeShareableCloneRecursive({
__init: () => {
'worklet';
return makeMutableUI(initial);
}
});
const mutable = {
get value() {
checkInvalidReadDuringRender();
const uiValueGetter = executeOnUIRuntimeSync(sv => {
return sv.value;
});
return uiValueGetter(mutable);
},
set value(newValue) {
checkInvalidWriteDuringRender();
runOnUI(() => {
mutable.value = newValue;
})();
},
get _value() {
throw new ReanimatedError('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) {
throw new ReanimatedError('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 ReanimatedError('Adding listeners is only possible on the UI runtime.');
},
removeListener: () => {
throw new ReanimatedError('Removing listeners is only possible on the UI runtime.');
},
_isReanimatedSharedValue: true
};
hideInternalValueProp(mutable);
addCompilerSafeGetAndSet(mutable);
shareableMappingCache.set(mutable, handle);
return mutable;
}
function makeMutableWeb(initial) {
let value = initial;
const listeners = new Map();
const mutable = {
get value() {
checkInvalidReadDuringRender();
return value;
},
set value(newValue) {
checkInvalidWriteDuringRender();
valueSetter(mutable, newValue);
},
get _value() {
return value;
},
set _value(newValue) {
value = newValue;
listeners.forEach(listener => {
listener(newValue);
});
},
modify: (modifier, forceUpdate = true) => {
valueSetter(mutable, modifier !== undefined ? modifier(mutable.value) : mutable.value, forceUpdate);
},
addListener: (id, listener) => {
listeners.set(id, listener);
},
removeListener: id => {
listeners.delete(id);
},
_isReanimatedSharedValue: true
};
hideInternalValueProp(mutable);
addCompilerSafeGetAndSet(mutable);
return mutable;
}
export const makeMutable = SHOULD_BE_USE_WEB ? makeMutableWeb : makeMutableNative;
//# sourceMappingURL=mutables.js.map
;