Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

106 строки
4.0 KiB

  1. /* @flow */
  2. import type {TransitionStyle} from './Types';
  3. // core keys merging algorithm. If previous render's keys are [a, b], and the
  4. // next render's [c, b, d], what's the final merged keys and ordering?
  5. // - c and a must both be before b
  6. // - b before d
  7. // - ordering between a and c ambiguous
  8. // this reduces to merging two partially ordered lists (e.g. lists where not
  9. // every item has a definite ordering, like comparing a and c above). For the
  10. // ambiguous ordering we deterministically choose to place the next render's
  11. // item after the previous'; so c after a
  12. // this is called a topological sorting. Except the existing algorithms don't
  13. // work well with js bc of the amount of allocation, and isn't optimized for our
  14. // current use-case bc the runtime is linear in terms of edges (see wiki for
  15. // meaning), which is huge when two lists have many common elements
  16. export default function mergeDiff(
  17. prev: Array<TransitionStyle>,
  18. next: Array<TransitionStyle>,
  19. onRemove: (prevIndex: number, prevStyleCell: TransitionStyle) => ?TransitionStyle
  20. ): Array<TransitionStyle> {
  21. // bookkeeping for easier access of a key's index below. This is 2 allocations +
  22. // potentially triggering chrome hash map mode for objs (so it might be faster
  23. // to loop through and find a key's index each time), but I no longer care
  24. let prevKeyIndex: {[key: string]: number} = {};
  25. for (let i = 0; i < prev.length; i++) {
  26. prevKeyIndex[prev[i].key] = i;
  27. }
  28. let nextKeyIndex: {[key: string]: number} = {};
  29. for (let i = 0; i < next.length; i++) {
  30. nextKeyIndex[next[i].key] = i;
  31. }
  32. // first, an overly elaborate way of merging prev and next, eliminating
  33. // duplicates (in terms of keys). If there's dupe, keep the item in next).
  34. // This way of writing it saves allocations
  35. let ret = [];
  36. for (let i = 0; i < next.length; i++) {
  37. ret[i] = next[i];
  38. }
  39. for (let i = 0; i < prev.length; i++) {
  40. if (!Object.prototype.hasOwnProperty.call(nextKeyIndex, prev[i].key)) {
  41. // this is called my TM's `mergeAndSync`, which calls willLeave. We don't
  42. // merge in keys that the user desires to kill
  43. const fill = onRemove(i, prev[i]);
  44. if (fill != null) {
  45. ret.push(fill);
  46. }
  47. }
  48. }
  49. // now all the items all present. Core sorting logic to have the right order
  50. return ret.sort((a, b) => {
  51. const nextOrderA = nextKeyIndex[a.key];
  52. const nextOrderB = nextKeyIndex[b.key];
  53. const prevOrderA = prevKeyIndex[a.key];
  54. const prevOrderB = prevKeyIndex[b.key];
  55. if (nextOrderA != null && nextOrderB != null) {
  56. // both keys in next
  57. return nextKeyIndex[a.key] - nextKeyIndex[b.key];
  58. } else if (prevOrderA != null && prevOrderB != null) {
  59. // both keys in prev
  60. return prevKeyIndex[a.key] - prevKeyIndex[b.key];
  61. } else if (nextOrderA != null) {
  62. // key a in next, key b in prev
  63. // how to determine the order between a and b? We find a "pivot" (term
  64. // abuse), a key present in both prev and next, that is sandwiched between
  65. // a and b. In the context of our above example, if we're comparing a and
  66. // d, b's (the only) pivot
  67. for (let i = 0; i < next.length; i++) {
  68. const pivot = next[i].key;
  69. if (!Object.prototype.hasOwnProperty.call(prevKeyIndex, pivot)) {
  70. continue;
  71. }
  72. if (nextOrderA < nextKeyIndex[pivot] && prevOrderB > prevKeyIndex[pivot]) {
  73. return -1;
  74. } else if (nextOrderA > nextKeyIndex[pivot] && prevOrderB < prevKeyIndex[pivot]) {
  75. return 1;
  76. }
  77. }
  78. // pluggable. default to: next bigger than prev
  79. return 1;
  80. }
  81. // prevOrderA, nextOrderB
  82. for (let i = 0; i < next.length; i++) {
  83. const pivot = next[i].key;
  84. if (!Object.prototype.hasOwnProperty.call(prevKeyIndex, pivot)) {
  85. continue;
  86. }
  87. if (nextOrderB < nextKeyIndex[pivot] && prevOrderA > prevKeyIndex[pivot]) {
  88. return 1;
  89. } else if (nextOrderB > nextKeyIndex[pivot] && prevOrderA < prevKeyIndex[pivot]) {
  90. return -1;
  91. }
  92. }
  93. // pluggable. default to: next bigger than prev
  94. return -1;
  95. });
  96. }