UNPKG

2.52 kBJavaScriptView Raw
1function shouldDeleteClassicInstanceMethod(component, name) {
2 if (component.__reactAutoBindMap && component.__reactAutoBindMap.hasOwnProperty(name)) {
3 // It's a known autobound function, keep it
4 return false;
5 }
6
7 if (component.__reactAutoBindPairs && component.__reactAutoBindPairs.indexOf(name) >= 0) {
8 // It's a known autobound function, keep it
9 return false;
10 }
11
12 if (component[name].__reactBoundArguments !== null) {
13 // It's a function bound to specific args, keep it
14 return false;
15 }
16
17 // It's a cached bound method for a function
18 // that was deleted by user, so we delete it from component.
19 return true;
20}
21
22function shouldDeleteModernInstanceMethod(component, name) {
23 const { prototype } = component.constructor;
24 const prototypeDescriptor = Object.getOwnPropertyDescriptor(prototype, name);
25
26 if (!prototypeDescriptor || !prototypeDescriptor.get) {
27 // This is definitely not an autobinding getter
28 return false;
29 }
30
31 if (prototypeDescriptor.get().length !== component[name].length) {
32 // The length doesn't match, bail out
33 return false;
34 }
35
36 // This seems like a method bound using an autobinding getter on the prototype
37 // Hopefully we won't run into too many false positives.
38 return true;
39}
40
41function shouldDeleteInstanceMethod(component, name) {
42 const descriptor = Object.getOwnPropertyDescriptor(component, name);
43 if (typeof descriptor.value !== 'function') {
44 // Not a function, or something fancy: bail out
45 return;
46 }
47
48 if (component.__reactAutoBindMap || component.__reactAutoBindPairs) {
49 // Classic
50 return shouldDeleteClassicInstanceMethod(component, name);
51 } else {
52 // Modern
53 return shouldDeleteModernInstanceMethod(component, name);
54 }
55}
56
57/**
58 * Deletes autobound methods from the instance.
59 *
60 * For classic React classes, we only delete the methods that no longer exist in map.
61 * This means the user actually deleted them in code.
62 *
63 * For modern classes, we delete methods that exist on prototype with the same length,
64 * and which have getters on prototype, but are normal values on the instance.
65 * This is usually an indication that an autobinding decorator is being used,
66 * and the getter will re-generate the memoized handler on next access.
67 */
68export default function deleteUnknownAutoBindMethods(component) {
69 const names = Object.getOwnPropertyNames(component);
70
71 names.forEach(name => {
72 if (shouldDeleteInstanceMethod(component, name)) {
73 delete component[name];
74 }
75 });
76}