From f423bedf1dd5d069684aa6b4d084e5b211740430 Mon Sep 17 00:00:00 2001 From: JinMuGo Date: Sun, 17 Mar 2024 22:23:18 +0900 Subject: [PATCH] FY-215/perf(changedBits): remove MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit changedBits 삭제 https://github.com/facebook/react/pull/20953 --- srcs/context/constructor/context.js | 17 +- srcs/context/constructor/contextItem.js | 11 +- srcs/context/core/contextCore.js | 4 +- srcs/context/createContext.js | 22 +- srcs/context/newContext.js | 34 +-- ....js => calculateChangedBits.js.deprecated} | 0 srcs/fiber/childFiber.js | 4 +- srcs/hooks/core/renderWithHooks.js | 3 +- srcs/hooks/shared/dispatchAction.js | 17 +- srcs/hooks/useContext/useContext.js | 4 +- srcs/hooks/useEffect/useEffectImpl.js | 1 - srcs/hooks/useReducer/useReducerImpl.js | 10 +- srcs/work/beginWork.js | 278 +----------------- type/TConsumer.js | 2 - type/TContext.js | 2 - type/TRfsType.js | 1 - 16 files changed, 39 insertions(+), 371 deletions(-) rename srcs/context/shared/{calculateChangedBits.js => calculateChangedBits.js.deprecated} (100%) diff --git a/srcs/context/constructor/context.js b/srcs/context/constructor/context.js index 7201558..eab6060 100644 --- a/srcs/context/constructor/context.js +++ b/srcs/context/constructor/context.js @@ -6,7 +6,6 @@ /** * * @param {Symbol} $$typeof - * @param {Function} _calculateChangedBits * @param {any} _currentValue * @param {any} _currentValue2 * @param {Number} _threadCount @@ -14,9 +13,8 @@ * @param {TConsumer} Consumer */ const context = class { - constructor($$typeof, _calculateChangedBits, _currentValue, _currentValue2, _threadCount, Provider, Consumer) { + constructor($$typeof, _currentValue, _currentValue2, _threadCount, Provider, Consumer) { this.$$typeof = $$typeof; - this._calculateChangedBits = _calculateChangedBits; this._currentValue = _currentValue; this._currentValue2 = _currentValue2; this._threadCount = _threadCount; @@ -28,23 +26,14 @@ const context = class { /** * * @param {Symbol} $$typeof - * @param {Function} _calculateChangedBits * @param {any} _currentValue * @param {any} _currentValue2 * @param {Number} _threadCount * @param {TProvider} Provider * @param {TConsumer} Consumer */ -const createContextInst = ( - $$typeof, - calculateChangedBits, - currentValue, - currentValue2, - threadCount, - Provider, - Consumer -) => { - return new context($$typeof, calculateChangedBits, currentValue, currentValue2, threadCount, Provider, Consumer); +const createContextInst = ($$typeof, currentValue, currentValue2, threadCount, Provider, Consumer) => { + return new context($$typeof, currentValue, currentValue2, threadCount, Provider, Consumer); }; export default createContextInst; diff --git a/srcs/context/constructor/contextItem.js b/srcs/context/constructor/contextItem.js index e43bc4f..f3f9617 100644 --- a/srcs/context/constructor/contextItem.js +++ b/srcs/context/constructor/contextItem.js @@ -6,13 +6,11 @@ /** * * @param {TContext} context - * @param {number} observedBits * @param {import("../../../type/TContextItem").TContextItem | null} next */ const contextItem = class { - constructor(context, observedBits, next) { + constructor(context, next) { this.context = context; - this.observedBits = observedBits; this.next = next; } }; @@ -20,11 +18,10 @@ const contextItem = class { /** * * @param {TContext} context - * @param {number} observedBits - * @param {import("../../../type/TContextItem").TContextItem | null} next + * @param {number, * @param {import("../../../type/TContextItem").TContextItem | null}} next */ -const createContextItem = (context, observedBits, next) => { - return new contextItem(context, observedBits, next); +const createContextItem = (context, next) => { + return new contextItem(context, next); }; export default createContextItem; diff --git a/srcs/context/core/contextCore.js b/srcs/context/core/contextCore.js index b0fda17..06df5b5 100644 --- a/srcs/context/core/contextCore.js +++ b/srcs/context/core/contextCore.js @@ -7,12 +7,12 @@ import { createCursor } from "../../fiber/fiberStack.js"; * 개발 모드에서 warning을 출력하기 위해 사용된다. * @property {Fiber | null} currentlyRenderingFiber - 현재 렌더링 중인 fiber를 가리킨다. * @property {TContextItem | null} lastContextDependency - 마지막으로 의존성을 가진 contextItem를 가리킨다. - * @property {TContext | null} lastContextWithAllBitsObserved - 마지막으로 모든 bit를 관찰한 context를 가리킨다. + * @property {TContext | null} lastFullyObservedContext - 마지막으로 완전히 관찰된 context를 가리킨다. */ export default { valueCursor: createCursor(null), rendererSigil: undefined, currentlyRenderingFiber: null, lastContextDependency: null, - lastContextWithAllBitsObserved: null, + lastFullyObservedContext: null, }; diff --git a/srcs/context/createContext.js b/srcs/context/createContext.js index 0fb45a5..b351a44 100644 --- a/srcs/context/createContext.js +++ b/srcs/context/createContext.js @@ -5,35 +5,17 @@ import createProvider from "./constructor/provider.js"; /** * * @param {any} defaultValue - * @param {Function | undefined} calculateChangedBits * @see ReactNewContext-test.internal.js 1055 line * * @description - 단순히 context 객체를 생성하는 함수입니다. 그 과정에서 * Provider와 Consumer를 생성합니다. * - * @description - calculateChangedBits - * 비트 마스킹을 사용하여 변경되었다면 Context.consumer를 re-render합니다. 그것을 - * 클라이언트 코드에서 조정할 수 있도록 직접 second argument로 넘겨줄 수 있습니다. - * 관련 링크는 아래에 있습니다. * @link https://dev.to/alexkhismatulin/react-context-a-hidden-power-3h8j * @returns */ -const createContext = (defaultValue, calculateChangedBits) => { - // client Code에서 calculateChangedBits를 넘겨주지 않았다면 null을 할당합니다. - if (calculateChangedBits === undefined) { - calculateChangedBits = null; - } - +const createContext = (defaultValue) => { // context Object를 생성합니다. - const context = createContextInst( - RFS_CONTEXT_TYPE, - calculateChangedBits, - defaultValue, - defaultValue, - 0, - null, - null - ); + const context = createContextInst(RFS_CONTEXT_TYPE, defaultValue, defaultValue, 0, null, null); // Provider 객체 생성. const provider = createProvider(RFS_PROVIDER_TYPE, context); diff --git a/srcs/context/newContext.js b/srcs/context/newContext.js index fd793d4..819984d 100644 --- a/srcs/context/newContext.js +++ b/srcs/context/newContext.js @@ -1,10 +1,9 @@ -import { MAX_SIGNED_31_BIT_INT } from "../const/CExpirationTime.js"; import { pop, push } from "../fiber/fiberStack.js"; import { markWorkInProgressReceivedUpdate } from "../work/beginWork.js"; import createContextItem from "./constructor/contextItem.js"; import contextCore from "./core/contextCore.js"; // TODO: import { isPrimaryRenderer } from "react-dom"; -// ReactDOMHostConfig에서 isPrimaryRenderer를 가져옵니다. +// RFS의 ReactDOMHostConfig에서 isPrimaryRenderer를 가져옵니다. /** * @@ -102,7 +101,7 @@ const scheduleWorkOnParentPath = (parent, renderExpirationTime) => { * // 이때 변경되었다는 것을 알리는 용도로 해당 fiber의 expirationTime을 변경합니다. * @returns */ -const propagateContextChange = (workInProgress, context, changedBits, renderExpirationTime) => { +const propagateContextChange = (workInProgress, context, renderExpirationTime) => { let fiber = workInProgress.child; if (fiber !== null) { // Set the return pointer of the child to the work-in-progress fiber. @@ -122,14 +121,9 @@ const propagateContextChange = (workInProgress, context, changedBits, renderExpi // Check if the context matches. // 현재 fiber의 context list중에서 현재 변경된 context와 일치하는 context가 있는지 확인합니다. // 만약에 일치하면서 변경되었다면 해당 fiber를 re-render해야합니다. - if (dependency.context === context && (dependency.observedBits & changedBits) !== 0) { + if (dependency.context === context) { // Match! Schedule an update on this fiber. - if (fiber.tag === ClassComponent) { - // NOTE: we don't implement ClassComponent. - // 저희는 함수형 컴포넌트만 사용하기 때문에 해당 부분은 구현하지 않습니다. - } - // fiber를 re-render하기 때문에 해당 fiber의 expirationTime을 변경합니다. if (fiber.expirationTime < renderExpirationTime) { fiber.expirationTime = renderExpirationTime; @@ -161,12 +155,10 @@ const propagateContextChange = (workInProgress, context, changedBits, renderExpi } else if (fiber.tag === ContextProvider) { // Don't scan deeper if this is a matching provider nextFiber = fiber.type === workInProgress.type ? null : fiber.child; - } else if (fiber.tag === DehydreatedFragment) { - // NOTE: we don't implement DehydreatedFragment. - // it's server components } else { nextFiber = fiber.child; } + // NOTE: we don't implement DehydreatedFragment. // nextFier가 null이라는 것은 더이상 child가 없다는 것. // 그러면 sibling을 확인하고 그마저도 존재하지 않는다면 parent로 올라갑니다. @@ -210,7 +202,7 @@ const propagateContextChange = (workInProgress, context, changedBits, renderExpi const prepareToReadContext = (workInProgress, renderExpirationTime) => { contextCore.currentlyRenderingFiber = workInProgress; contextCore.lastContextDependency = null; - contextCore.lastContextWithAllBitsObserved = null; + contextCore.lastFullyObservedContext = null; // component는 여러개의 context를 사용할 수 있습니다. // dependencies는 해당 fiber에서 사용되는 컨텍스트의 리스트입니다. @@ -240,21 +232,11 @@ const prepareToReadContext = (workInProgress, renderExpirationTime) => { * 이 함수에서 context의 값을 읽습니다. * @returns */ -const readContext = (context, observedBits) => { - if (contextCore.lastContextWithAllBitsObserved === context) { +const readContext = (context) => { + if (contextCore.lastFullyObservedContext === context) { // Nothing to do. We already observe everything in this context. - } else if (observedBits === false || observedBits === 0) { - // Do not observe any updates. } else { - let resolvedObservedBits; - if (typeof observedBits !== "number" || observedBits === MAX_SIGNED_31_BIT_INT) { - contextCore.lastContextWithAllBitsObserved = context; - resolvedObservedBits = MAX_SIGNED_31_BIT_INT; - } else { - resolvedObservedBits = observedBits; - } - - const contextItem = createContextItem(context, resolvedObservedBits, null); + const contextItem = createContextItem(context, null); if (contextCore.lastContextDependency === null) { // this is the first dependency for this component. Create a new list. diff --git a/srcs/context/shared/calculateChangedBits.js b/srcs/context/shared/calculateChangedBits.js.deprecated similarity index 100% rename from srcs/context/shared/calculateChangedBits.js rename to srcs/context/shared/calculateChangedBits.js.deprecated diff --git a/srcs/fiber/childFiber.js b/srcs/fiber/childFiber.js index 54ad292..15ddcc6 100644 --- a/srcs/fiber/childFiber.js +++ b/srcs/fiber/childFiber.js @@ -552,8 +552,8 @@ const ChildReconciler = (shouldTrackSideEffects) => { //그리고 마지막으로 배치가 바뀌지 않은 파이버의 인덱스를 가르키는 변수를 업데이트해야됨 //NOTE: place-배치란 list에서의 배치를 의미함-> 이것은 array에서 index를 바꾸는 것이 //NOTE:아닌 인접한것을 기준을 의미함 (fiber 자체가 list구조이므로) - //NOTE: placeChild의 알고리즘은 placeChild를 참고하면 - + //NOTE: placeChild의 알고리즘은 placeChild를 참고하면 + lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx); //딱 한번만 resultFirstFiber를 세팅해야됨 diff --git a/srcs/hooks/core/renderWithHooks.js b/srcs/hooks/core/renderWithHooks.js index 18e48ae..185fffe 100644 --- a/srcs/hooks/core/renderWithHooks.js +++ b/srcs/hooks/core/renderWithHooks.js @@ -27,7 +27,6 @@ const finishRenderingHooks = (hookCore, hookExpirationTime) => { hookCore.sideEffectTag = 0; // finish hookExpirationTime - // TODO: import NoWork hookExpirationTime.renderExpirationTime = NoWork; hookExpirationTime.remainingExpirationTime = NoWork; }; @@ -55,7 +54,7 @@ const renderWithHooks = (current, workInProgress, Component, props, refOrContext } // Component render - // TODO: ?? 그러면 props랑 context랑 같이 들어오는데? 이게 뭐지. + // NOTE: refOrContext를 넣어주는 이유는 ClassComponent 때문. let children = Component(props, refOrContext); // render phase시에 update가 발생했다면 해당 Component를 다시 렌더링합니다. diff --git a/srcs/hooks/shared/dispatchAction.js b/srcs/hooks/shared/dispatchAction.js index 3c9e847..95f69d9 100644 --- a/srcs/hooks/shared/dispatchAction.js +++ b/srcs/hooks/shared/dispatchAction.js @@ -1,9 +1,10 @@ import { NoWork } from "../../const/CExpirationTime.js"; import is from "../../shared/objectIs.js"; +import { computeExpirationForFiber, requestCurrentTimeForUpdate, scheduleWork } from "../../work/workloop.js"; import createHookUpdate from "../constructor/hookUpdate.js"; import hookCore from "../core/hookCore.js"; import hookExpirationTime from "../core/hookExpirationTime.js"; -import hookRenderPhase from "../core/hookRenderPhase.js"; +import hookRenderPhase, { RE_RENDER_LIMIT } from "../core/hookRenderPhase.js"; import enqueueRenderPhaseUpdate from "./enqueueRenderPhaseUpdate.js"; /** @@ -26,6 +27,10 @@ import enqueueRenderPhaseUpdate from "./enqueueRenderPhaseUpdate.js"; */ const dispatchAction = (fiber, queue, action) => { + if (hookRenderPhase.numberOfReRenders < RE_RENDER_LIMIT) { + throw new Error("Too many re-renders. React limits the number of renders to prevent an infinite loop."); + } + const alternate = fiber.alternate; // TODO: isRenderPhaseUpdate 함수로 Refactor @@ -64,14 +69,9 @@ const dispatchAction = (fiber, queue, action) => { } } else { // this is an idle status update - - // TODO: Implement this function. requestCurrentTimeForUpdate const currentTime = requestCurrentTimeForUpdate(); - // TODO: Implement this function. requestCurrentSuspenseConfig 사용하지 않을 수 있음 - const suspenseConfig = requestCurrentSuspenseConfig(); - // TODO: Implement this function. computeExpirationForFiber // hookupdate에서 suspenseConfig를 사용하고 있는데 이후 updateReducer의 markRenderEventTimeAndConfig에서만 사용된다 - const expirationTime = computeExpirationForFiber(currentTime, fiber, suspenseConfig); + const expirationTime = computeExpirationForFiber(currentTime, fiber); const update = createHookUpdate(expirationTime, suspenseConfig, action, null, null, null); enqueueRenderPhaseUpdate(queue, update); @@ -94,7 +94,7 @@ const dispatchAction = (fiber, queue, action) => { // The queue is currently empty, which means we can eagerly compute the // next state before entering the render phase. If the new state is the // same as the current state, we may be able to bail out entirely. - //NOTE:여기서의 Bailout은 scheduleWork를 호출하지 않는다는 것인듯. + //NOTE: 여기서의 Bailout은 scheduleWork를 호출하지 않는다는 것인듯. const lastRenderedReducer = queue.lastRenderedReducer; if (lastRenderedReducer !== null) { try { @@ -113,7 +113,6 @@ const dispatchAction = (fiber, queue, action) => { } } } - // TODO: Implement this function. scheduleWork scheduleWork(fiber, expirationTime); } }; diff --git a/srcs/hooks/useContext/useContext.js b/srcs/hooks/useContext/useContext.js index 47caadf..37a6d90 100644 --- a/srcs/hooks/useContext/useContext.js +++ b/srcs/hooks/useContext/useContext.js @@ -11,8 +11,8 @@ import hookCore from "../core/hookCore.js"; * @param {TContext} context * @param {number | undefined} observedBits */ -const useContext = (context, observedBits) => { - return hookCore.RfsCurrentDispatcher.current.useContext(context, observedBits); +const useContext = (context) => { + return hookCore.RfsCurrentDispatcher.current.useContext(context); }; export default useContext; diff --git a/srcs/hooks/useEffect/useEffectImpl.js b/srcs/hooks/useEffect/useEffectImpl.js index 328408b..7c52d9a 100644 --- a/srcs/hooks/useEffect/useEffectImpl.js +++ b/srcs/hooks/useEffect/useEffectImpl.js @@ -91,7 +91,6 @@ export const updateEffectImpl = (fiberFlags, hookFlags, create, deps) => { //가정4. 지우지 못하는 이유는 circularlist //가정4. (모름) 가르키는 곳을 바꿔서 거기부터 처리한다. pushEffect(NoHookEffect, create, inst, nextDeps); - // NOTE: 16.12.0에서는 새롭게 생성된 effect를 사용하지 않는데, 이건 reconciler쪽을 봐야할 것 같다. return; } } diff --git a/srcs/hooks/useReducer/useReducerImpl.js b/srcs/hooks/useReducer/useReducerImpl.js index 5cdf4e2..e626860 100644 --- a/srcs/hooks/useReducer/useReducerImpl.js +++ b/srcs/hooks/useReducer/useReducerImpl.js @@ -10,6 +10,8 @@ import { mountWorkInProgressHook, updateWorkInProgressHook } from "../core/workI import { createHookUpdateQueue } from "../constructor/index.js"; import hookExpirationTime from "../core/hookExpirationTime.js"; import dispatchAction from "../shared/dispatchAction.js"; +import { markWorkInProgressReceivedUpdate } from "../../work/beginWork.js"; +import { markRenderEventTimeAndConfig, markUnprocessedUpdateTime } from "../../work/workloop.js"; /** * @param {THookObject} hook @@ -55,9 +57,6 @@ const updateReducerImpl = (hook, reducer) => { } while (update !== null); if (is(newState, hook.memoizedState) === false) { - // TODO: Implement this function. - // 함수 구현이 간단하긴 하지만 beginWork의 module scope를 참조해야 하기 때문에 - // 이후 추가 구현을 위해 주석처리 하였습니다 markWorkInProgressReceivedUpdate(); } @@ -115,13 +114,11 @@ const updateReducerImpl = (hook, reducer) => { if (updateExpirationTime > hookExpirationTime.remainingExpirationTime) { hookExpirationTime.remainingExpirationTime = updateExpirationTime; - // TODO: Implement this function. markUnprocessedUpdateTime(hookExpirationTime.remainingExpirationTime); } } else { // This update does have sufficient priority. - // TODO: implement this function. - markRenderEventTimeAndConfig(updateExpirationTime, update.suspenseConfig); + markRenderEventTimeAndConfig(updateExpirationTime); if (update.eagerReducer === reducer) { newState = update.eagerState; @@ -140,7 +137,6 @@ const updateReducerImpl = (hook, reducer) => { } if (!is(newState, hook.memoizedState)) { - // TODO: Implement this function. markWorkInProgressReceivedUpdate(); } diff --git a/srcs/work/beginWork.js b/srcs/work/beginWork.js index 09cde2e..e0f7970 100644 --- a/srcs/work/beginWork.js +++ b/srcs/work/beginWork.js @@ -1,274 +1,6 @@ import { prepareToReadContext, propagateContextChange, pushProvider, readContext } from "../context/newContext.js"; -import calculateChangedBits from "../context/shared/calculateChangedBits.js"; import hasContextChanged from "../context/shared/hasContextChanged.js"; -import { - IndeterminateComponent, - FunctionComponent, - HostRoot, - HostComponent, - HostText, - Fragment, - ContextProvider, - SimpleMemoComponent, -} from "../const/CWorkTag.js"; -import { Placement, PerformedWork } from "../const/CSideEffectFlags.js"; -import { Update as UpdateEffect, Passive as PassiveEffect } from "../const/CSideEffectFlags.js"; -import { cloneChildFibers, reconcileChildFibers, mountChildFibers } from "../fiber/childFiber.js"; -import renderWithHooks from "../hooks/core/renderWithHooks.js"; -import { shallowEqual } from "../shared/sharedEqual.js"; -import { markUnprocessedUpdateTime } from "./workloop.js"; -import { pushHostContainer, pushHostContext } from "../fiber/fiberHostContext.js"; -import { processUpdateQueue } from "../core/UpdateQueue.js"; - -/** - * - * @param {TFiber|null} current - * @param {TFiber} workInProgress - * @param {any} nextChildren - * @param {TExpirationTime} renderExpirationTime - * @description 해당함수는 현재 파이버의 자식을 재조정하는 함수입니다. - */ -export const reconcileChildren = (current, workInProgress, nextChildren, renderExpirationTime) => { - if (current === null) { - //TODO: mountChildFibers 구현 - // 아직 렌더링되지 않은 새 컴포넌트인 경우, 우리는 - // 최소한의 sideEffect을 적용하여 자식 집합을 업데이트하지 않습니다. 대신 - // 렌더링되기 전에 자식에 모두 추가합니다(모든 사이드 이펙트를 가함). 즉 - // 부작용을 추적하지 않음으로써 이 조정 패스를 최적화할 수 있습니다. - workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderExpirationTime); - } else { - //TODO: reconcileChildFibers 구현 - // 현재 자식이 진행 중인 작업과 동일하면 다음을 의미합니다. - // 아직 이 자식에 대한 작업을 시작하지 않았다는 뜻입니다. 따라서 우리는 - // 복제 알고리즘을 사용하여 현재 모든 자식의 복사본을 만듭니다. - - // 이미 진행 중인 작업이 있다면 이 시점에서는 유효하지 않으므로 - // 버리겠습니다. - workInProgress.child = reconcileChildFibers(workInProgress, current.child, nextChildren, renderExpirationTime); - } -}; -/** - * @param {TFiber} current - * @param {TFiber} workInProgress - * @param {TFiber} renderExpirationTime - * @returns {TFiber|null} - * @description HostRootComponent를 업데이트하는 함수 - */ -const updateHostRoot = (current, workInProgress, renderExpirationTime) => { - //파이버스택에 현재 파이버루트의 컨텍스트를 넣어준다. - pushHostRootContext(workInProgress); - const updateQueue = workInProgress.updateQueue; - const nextProps = workInProgress.pendingProps; - - //updateQueue에서(processUpdateQueue)에서 기록한 state를 보관하고 있음 - const prevState = workInProgress.memoizedState; - //updateContainer에서 update.payload = {element}이런식으로 payload를 보관하고 - //process되면 memoizedState가 이 updateState를 가르킬텐데-> 그렇게 되면 - //prevState.element에는 payload의 element:element이런식으로 프로퍼티 형태로 들어가있음 - const prevChildren = prevState !== null ? prevState.element : null; - - //updateQueue를 처리한다 - processUpdateQueue(workInProgress, updateQueue, nextProps, renderExpirationTime); - //updateQueue를 처리한 후에는 memoizedState가 바뀌었을 수 있으므로 다시 가져온다. - const nextState = workInProgress.memoizedState; - const nextChildren = nextState.element; - //이전 자식과 같으면 bailout - if (nextChildren === prevChildren) { - return bailoutOnAlreadyFinishedWork(current, workInProgress, renderExpirationTime); - } - reconcileChildren(current, workInProgress, nextChildren, renderExpirationTime); - return workInProgress.child; -}; - -/** - * - * @param {TFiber} current - * @param {TFiber} workInProgress - * @description ref가 교체 되었으면 effectTag에 Ref를 업데이트 해야된다라고 마킹하는 함수 - */ -const markRef = (current, workInProgress) => { - const ref = workInProgress.ref; - if ((current === null && ref !== null) || (current !== null && current.ref !== ref)) { - workInProgress.effectTag |= Ref; - } -}; - -/** - * - * @param {TFiber} current - * @param {TFiber} workInProgress - * @param {TExpirationTime} renderExpirationTime - * @returns {TFiber | null} - * @description HostComponent를 업데이트하는 함수 호스트 컴포넌트는 문자열하나만 자식으로 가지고 있을떄 특수처리합니다. - */ -const updateHostComponent = (current, workInProgress, renderExpirationTime) => { - //파이버스택에 현재 hostContext를 푸시한다. - pushHostContext(workInProgress); - - const type = workInProgress.type; - const nextProps = workInProgress.pendingProps; - const prevProps = current !== null ? current.memoizedProps : null; - - let nextChildren = nextProps.children; - //TODO: shouldSetTextContent 구현 DOM모듈에서 - //호스트 컴포넌트는 자식으로 문자열 하나만 가지고 있을떄 해당 문자열을 파이버로 만들지 않음. - //TODO: 위에 설명확인 - const isDirectTextChild = shouldSetTextContent(type, nextProps); - if (isDirectTextChild) { - //이것은 문자열을 파이버로 만들지 않기 위해 초기화합니다. - // 호스트 노드의 직접 텍스트 자식을 특수 처리합니다. 이것은 일반적인 - // 케이스입니다. 재정의된 자식으로 처리하지 않습니다. 대신 이 프로퍼티에 액세스할 수 있는 호스트 환경에서 - // 이 프로퍼티에 액세스할 수 있는 호스트 환경에서 처리합니다. 그 - // 다른 HostText 파이버를 할당하고 순회하는 것을 방지합니다. - nextChildren = null; - } else if (prevProps !== null && shouldSetTextContent(type, prevProps)) { - // 직접 텍스트 자식에서 일반 자식으로 전환하는 경우 또는 - // 비어있는 경우 텍스트 콘텐츠가 재설정되도록 예약해야 합니다. - - //여기서 문자열이 아닌 다음 자식을 삽입하기전에 current가 문자열만 가졌다면 - //제거해주고 삭제해야됨 - //그러나 fiber로 따로 만들지 않았기 떄문에 삭제 로직을 타지 않는데 - //이를 따로 처리해주기 위해 ContentReset을 마킹해준다. - workInProgress.effectTag |= ContentReset; - } - - //hostComponent가 업데이트 되면 ref도 업데이트되기 떄문에 마킹 - markRef(current, workInProgress); - - //NOTE: check OffScreen 구현해야 되는지 확인 - - reconcileChildren(current, workInProgress, nextChildren, renderExpirationTime); - return workInProgress.child; -}; -/** - * - * @param {TFiber | null} _current @see 파일경로: /type/TFiber.js - * @param {TFiber} workInProgress @see 파일경로: /type/TFiber.js - * @param {lambda} Component - * @param {TExpirationTime} renderExpirationTime @see 파일경로: /type/TExpirationTime.js - * @returns {TFiber|null} @see 파일경로: /type/TFiber.js - * @description function Component를 마운트하는 함수 - */ -const mountIndeterminateComponent = (_current, workInProgress, Component, renderExpirationTime) => { - if (_current !== null) { - //NOTE: 오직 susPense가 일어난 상황에서만 일어나는 로직 - 빼야되는지 확인 - // An indeterminate component only mounts if it suspended inside a non- - // concurrent tree, in an inconsistent state. We want to treat it like - // a new mount, even though an empty version of it already committed. - // Disconnect the alternate pointers. - _current.alternate = null; - workInProgress.alternate = null; - // Since this is conceptually a new fiber, schedule a Placement effect - workInProgress.effectTag |= Placement; - } - - const props = workInProgress.pendingProps; - - //context를 사용하기 전에 context의 해당 값들을 초기화 해주는 함수 - prepareToReadContext(workInProgress, renderExpirationTime); - const value = renderWithHooks(null, workInProgress, Component, props, null, renderExpirationTime); - - workInProgress.effectTag |= PerformedWork; - - if ( - typeof value === "object" && - value !== null && - typeof value.render === "function" && - value.$$typeof === undefined - ) { - console.error("not supported ClassComponent in mountIndeterminateComponent"); - throw new Error("not supported ClassComponent in mountIndeterminateComponent"); - } else { - workInProgress.tag = FunctionComponent; - reconcileChildren(null, workInProgress, value, renderExpirationTime); - return workInProgress.child; - } -}; - -/** - * - * @param {TFiber} current - * @param {TFiber} workInProgress - * @param {TExpirationTime} expirationTime - * @description update를 하게되면 component를 실행시키게 되는데 업데이트가 안되면 그 전 관련 - * @description 훅작업을 취소 시켜야됨 그것의 역할을 하는 함수 - */ -const bailoutHooks = (current, workInProgress, expirationTime) => { - //훅의 잔여물을 제거하는 역할 - workInProgress.updateQueue = current.updateQueue; - //passiveEffect랑 updateEffect를 제거하는 역할 - workInProgress.effectTag &= ~(PassiveEffect | UpdateEffect); - //현재 expirationTime보다 이후것 생겼으면 잔여물->제거 - if (current.expirationTime <= expirationTime) { - current.expirationTime = NoWork; - } -}; - -/** - * @param {TFiber | null} _current - * @param {TFiber} workInProgress - * @param {lambda} Component - * @param {any} nextProps - * @param {TExpirationTime} renderExpirationTime - * @returns {TFiber|null} - * @description function Component를 업데이트하는 함수 - */ -const updateFunctionComponent = (_current, workInProgress, Component, nextProps, renderExpirationTime) => { - prepareToReadContext(workInProgress, renderExpirationTime); - const nextChildren = renderWithHooks(_current, workInProgress, Component, nextProps, null, renderExpirationTime); - - // beginWork()에서 확인한 결과 props는 변경되지 않았다. - // 하지만 업데이트가 발생한 컴포넌트이므로 호출되어야 한다. - // 컴포넌트 호출 후에도 didReceiveUpdate는 여전히 false임을 미루어보아 컴포넌트 상태 또한 변경되지 않았다. - // props, state 모두 변경되지 않았다면 서브 트리 또한 변경될 부분이 없으므로 Work를 여기서 끊어주게 될 경우 불필요한 작업을 하지 않아도 된다. - // 문제는 컴포넌트가 한번은 호출되었기 때문에 라이프 사이클 훅(useEffect(), useLayoutEffect())의 잔여물이 fiber에 남아있다. - // 잔여물을 제거하고 bailoutOnAlreadyFinishedWork()를 진행한다. 만약 자손에서 업데이트 컴포넌트가 있다면 자식 workInProgress가 반환되어 계속해서 밑으로 Work가 진행될 것이지만, 없다면 여기서 Work는 종료된다. - if (_current !== null && !didReceiveUpdate) { - bailoutHooks(_current, workInProgress, renderExpirationTime); - return bailoutOnAlreadyFinishedWork(_current, workInProgress, renderExpirationTime); - } - - workInProgress.effectTag |= PerformedWork; - reconcileChildren(_current, workInProgress, nextChildren, renderExpirationTime); - return workInProgress.child; -}; - -/** - * - * @param {TFiber|null} current @see 파일경로: /type/TFiber.js - * @param {TFiber} workInProgress @see 파일경로: /type/TFiber.js - * @param {TExpirationTime} renderExpirationTime @see 파일경로: /type/TExpirationTime.js - * @description 1.컴포넌트의 props나 state가 변경되지 않았을 때 - * @description 1.a이미 처리된 작업의 결과를 재사용할 수 있을 때 - * @description 1.a.가)더 높은 우선순위의 작업이 있어서 현재 작업을 나중으로 미룰 필요가 있을 때 - * @description 사용 되며, 이전 작업을 재사용하고(자식의 childExtime도 update필요 없을떄), 필요하다면 자식만 clone하여 사용합니다. - * @returns {TFiber|null} @see 파일경로: /type/TFiber.js - */ -const bailoutOnAlreadyFinishedWork = (current, workInProgress, renderExpirationTime) => { - if (current !== null) { - // Reuse previous dependencies - //dependencies는 현재 파이버의 관련 contextList라할 수 있는데 그것을 - //current에 있는걸 그대로 가져온다.-> context리스트도 업데이트 되지 않으니까 - workInProgress.dependencies = current.dependencies; - } - - //update가 뒤로 밀린 상황이라면 나중에 처리하기 위해 mark를 해준다. - const updateExpirationTime = workInProgress.expirationTime; - if (updateExpirationTime !== NoWork) { - markUnprocessedUpdateTime(updateExpirationTime); - } - - const childExpirationTime = workInProgress.childExpirationTime; - if (childExpirationTime < renderExpirationTime) { - //자식들도 업데이트가 되지 않았음. - return null; - } else { - // 이 파이버에는 작업이 없지만 그 하위 트리에는 작업이 있습니다. 자식 - // 파이버를 복제하고 계속합니다. - cloneChildFibers(current, workInProgress); - return workInProgress.child; - } -}; +import is from "../shared/objectIs.js"; let didReceiveUpdate = false; @@ -306,9 +38,7 @@ const updateContextProvider = (current, workInProgress, renderExpirationTime) => if (oldProps !== null) { const oldValue = oldProps.value; - // calculateChangedBits를 통해 이전 값과 새로운 값이 변하였는지 계산합니다. - const changedBits = calculateChangedBits(context, newValue, oldValue); - if (changedBits === 0) { + if (is(oldValue, newValue)) { // No change. Bailout early if children are the same. if (oldProps.children === newProps.children && !hasContextChanged()) { return bailoutOnAlreadyFinishedWork(current, workInProgress, renderExpirationTime); @@ -319,7 +49,7 @@ const updateContextProvider = (current, workInProgress, renderExpirationTime) => // changedBits가 0이 아니라면, context가 변했다는 것을 의미합니다. // Provider의 값이 변경되었기 때문에 해당 Provider의 값을 사용하는 Consumer를 찾아서 // re-render를 해야합니다. - propagateContextChange(workInProgress, context, changedBits, renderExpirationTime); + propagateContextChange(workInProgress, context, renderExpirationTime); } } @@ -349,7 +79,7 @@ const updateContextConsumer = (current, workInProgress, renderExpirationTime) => prepareToReadContext(workInProgress, renderExpirationTime); // readContext를 통해 context 값을 가져옵니다. - const newValue = readContext(context, newProps.unstable_observedBits); + const newValue = readContext(context); // child component를 context의 값을 넣어 호출합니다. const newChildren = render(newValue); diff --git a/type/TConsumer.js b/type/TConsumer.js index 261e727..888fe34 100644 --- a/type/TConsumer.js +++ b/type/TConsumer.js @@ -2,10 +2,8 @@ * @typedef {Object} TConsumer * @property {Symbol} $$typeof * @property {TContext} _context - * @property {Function} _calculateChangedBits */ const TConsumer = { $$typeof: Symbol, _context: TContext, - _calculateChangedBits: Function, }; diff --git a/type/TContext.js b/type/TContext.js index 8ef62c7..2a39c88 100644 --- a/type/TContext.js +++ b/type/TContext.js @@ -4,14 +4,12 @@ * @property {Symbol | number} $$typeof - React Context Type * @property {ReactProviderType} Provider - React Provider Type * @property {ReactConsumer} Consumer - React Consumer Type - * @property {((a: T, b: T) => number) | null} _calculateChangedBits - Calculate Changed Bits * @property {T} _currentValue - Current Value * @property {T} _currentValue2 - Current Value 2 * @property {number} _threadCount - Thread Count */ const TContext = { $$typeof: Symbol, - _calculateChangedBits: Function, _currentValue: any, _currentValue2: any, diff --git a/type/TRfsType.js b/type/TRfsType.js index 4dd1300..35a8f3c 100644 --- a/type/TRfsType.js +++ b/type/TRfsType.js @@ -28,7 +28,6 @@ const TRfsContext = { $$typeof: Symbol | Number, Consumer: TRfsContext, Provider: TRfsProviderType, - // _calculateChangedBits: ((a: T, b: T) => Number) | null, _currentValue: T, _currentValue2: T, _threadCount: Number,