aboutsummaryrefslogtreecommitdiff
path: root/node_modules/react-dom/lib/ReactChildFiber.js
blob: c78c63809f9fc260b2a0f0da6b892da5cb67f8e3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * 
 */

'use strict';

var REACT_ELEMENT_TYPE = require('./ReactElementSymbol');

var _require = require('./ReactCoroutine'),
    REACT_COROUTINE_TYPE = _require.REACT_COROUTINE_TYPE,
    REACT_YIELD_TYPE = _require.REACT_YIELD_TYPE;

var ReactFiber = require('./ReactFiber');
var ReactReifiedYield = require('./ReactReifiedYield');

var cloneFiber = ReactFiber.cloneFiber,
    createFiberFromElement = ReactFiber.createFiberFromElement,
    createFiberFromCoroutine = ReactFiber.createFiberFromCoroutine,
    createFiberFromYield = ReactFiber.createFiberFromYield;
var createReifiedYield = ReactReifiedYield.createReifiedYield;


var isArray = Array.isArray;

function ChildReconciler(shouldClone) {

  function createSubsequentChild(returnFiber, existingChild, previousSibling, newChildren, priority) {
    if (typeof newChildren !== 'object' || newChildren === null) {
      return previousSibling;
    }

    switch (newChildren.$$typeof) {
      case REACT_ELEMENT_TYPE:
        {
          var element = newChildren;
          if (existingChild && element.type === existingChild.type && element.key === existingChild.key) {
            // TODO: This is not sufficient since previous siblings could be new.
            // Will fix reconciliation properly later.
            var clone = shouldClone ? cloneFiber(existingChild, priority) : existingChild;
            if (!shouldClone) {
              // TODO: This might be lowering the priority of nested unfinished work.
              clone.pendingWorkPriority = priority;
            }
            clone.pendingProps = element.props;
            clone.sibling = null;
            clone['return'] = returnFiber;
            previousSibling.sibling = clone;
            return clone;
          }
          var child = createFiberFromElement(element, priority);
          previousSibling.sibling = child;
          child['return'] = returnFiber;
          return child;
        }

      case REACT_COROUTINE_TYPE:
        {
          var coroutine = newChildren;
          var _child = createFiberFromCoroutine(coroutine, priority);
          previousSibling.sibling = _child;
          _child['return'] = returnFiber;
          return _child;
        }

      case REACT_YIELD_TYPE:
        {
          var yieldNode = newChildren;
          var reifiedYield = createReifiedYield(yieldNode);
          var _child2 = createFiberFromYield(yieldNode, priority);
          _child2.output = reifiedYield;
          previousSibling.sibling = _child2;
          _child2['return'] = returnFiber;
          return _child2;
        }
    }

    if (isArray(newChildren)) {
      var prev = previousSibling;
      var existing = existingChild;
      for (var i = 0; i < newChildren.length; i++) {
        var nextExisting = existing && existing.sibling;
        prev = createSubsequentChild(returnFiber, existing, prev, newChildren[i], priority);
        if (prev && existing) {
          // TODO: This is not correct because there could've been more
          // than one sibling consumed but I don't want to return a tuple.
          existing = nextExisting;
        }
      }
      return prev;
    } else {
      // TODO: Throw for unknown children.
      return previousSibling;
    }
  }

  function createFirstChild(returnFiber, existingChild, newChildren, priority) {
    if (typeof newChildren !== 'object' || newChildren === null) {
      return null;
    }

    switch (newChildren.$$typeof) {
      case REACT_ELEMENT_TYPE:
        {
          /* $FlowFixMe(>=0.31.0): This is an unsafe cast. Consider adding a type
           *                       annotation to the `newChildren` param of this
           *                       function.
           */
          var element = newChildren;
          if (existingChild && element.type === existingChild.type && element.key === existingChild.key) {
            // Get the clone of the existing fiber.
            var clone = shouldClone ? cloneFiber(existingChild, priority) : existingChild;
            if (!shouldClone) {
              // TODO: This might be lowering the priority of nested unfinished work.
              clone.pendingWorkPriority = priority;
            }
            clone.pendingProps = element.props;
            clone.sibling = null;
            clone['return'] = returnFiber;
            return clone;
          }
          var child = createFiberFromElement(element, priority);
          child['return'] = returnFiber;
          return child;
        }

      case REACT_COROUTINE_TYPE:
        {
          /* $FlowFixMe(>=0.31.0): No 'handler' property found in object type
           */
          var coroutine = newChildren;
          var _child3 = createFiberFromCoroutine(coroutine, priority);
          _child3['return'] = returnFiber;
          return _child3;
        }

      case REACT_YIELD_TYPE:
        {
          // A yield results in a fragment fiber whose output is the continuation.
          // TODO: When there is only a single child, we can optimize this to avoid
          // the fragment.
          /* $FlowFixMe(>=0.31.0): No 'continuation' property found in object
           * type
           */
          var yieldNode = newChildren;
          var reifiedYield = createReifiedYield(yieldNode);
          var _child4 = createFiberFromYield(yieldNode, priority);
          _child4.output = reifiedYield;
          _child4['return'] = returnFiber;
          return _child4;
        }
    }

    if (isArray(newChildren)) {
      var first = null;
      var prev = null;
      var existing = existingChild;
      /* $FlowIssue(>=0.31.0) #12747709
       *
       * `Array.isArray` is matched syntactically for now until predicate
       * support is complete.
       */
      for (var i = 0; i < newChildren.length; i++) {
        var nextExisting = existing && existing.sibling;
        if (prev == null) {
          prev = createFirstChild(returnFiber, existing, newChildren[i], priority);
          first = prev;
        } else {
          prev = createSubsequentChild(returnFiber, existing, prev, newChildren[i], priority);
        }
        if (prev && existing) {
          // TODO: This is not correct because there could've been more
          // than one sibling consumed but I don't want to return a tuple.
          existing = nextExisting;
        }
      }
      return first;
    } else {
      // TODO: Throw for unknown children.
      return null;
    }
  }

  // TODO: This API won't work because we'll need to transfer the side-effects of
  // unmounting children to the returnFiber.
  function reconcileChildFibers(returnFiber, currentFirstChild, newChildren, priority) {
    return createFirstChild(returnFiber, currentFirstChild, newChildren, priority);
  }

  return reconcileChildFibers;
}

exports.reconcileChildFibers = ChildReconciler(true);

exports.reconcileChildFibersInPlace = ChildReconciler(false);

function cloneSiblings(current, workInProgress, returnFiber) {
  workInProgress['return'] = returnFiber;
  while (current.sibling) {
    current = current.sibling;
    workInProgress = workInProgress.sibling = cloneFiber(current, current.pendingWorkPriority);
    workInProgress['return'] = returnFiber;
  }
  workInProgress.sibling = null;
}

exports.cloneChildFibers = function (current, workInProgress) {
  if (!workInProgress.child) {
    return;
  }
  if (current && workInProgress.child === current.child) {
    // We use workInProgress.child since that lets Flow know that it can't be
    // null since we validated that already. However, as the line above suggests
    // they're actually the same thing.
    var currentChild = workInProgress.child;
    // TODO: This used to reset the pending priority. Not sure if that is needed.
    // workInProgress.pendingWorkPriority = current.pendingWorkPriority;
    // TODO: The below priority used to be set to NoWork which would've
    // dropped work. This is currently unobservable but will become
    // observable when the first sibling has lower priority work remaining
    // than the next sibling. At that point we should add tests that catches
    // this.
    var newChild = cloneFiber(currentChild, currentChild.pendingWorkPriority);
    workInProgress.child = newChild;
    cloneSiblings(currentChild, newChild, workInProgress);
  }

  // If there is no alternate, then we don't need to clone the children.
  // If the children of the alternate fiber is a different set, then we don't
  // need to clone. We need to reset the return fiber though since we'll
  // traverse down into them.
  var child = workInProgress.child;
  while (child) {
    child['return'] = workInProgress;
    child = child.sibling;
  }
};