From a8295ecac211a869620834f5a74a06b3af2ddf85 Mon Sep 17 00:00:00 2001 From: chenjianfeng Date: Tue, 14 Nov 2023 21:26:06 +0800 Subject: [PATCH] feat: support useState --- dist/node_modules/react-dom/client.js | 699 ++++++++++++++++++ dist/node_modules/react-dom/index.js | 699 ++++++++++++++++++ dist/node_modules/react-dom/package.json | 9 + dist/node_modules/react/index.js | 81 ++ dist/node_modules/react/jsx-dev-runtime.js | 90 +++ dist/node_modules/react/jsx-runtime.js | 90 +++ dist/node_modules/react/package.json | 6 + packages/react-dom/src/root.ts | 2 +- packages/react-reconciler/src/childFiber.ts | 69 +- packages/react-reconciler/src/completeWork.ts | 23 +- packages/react-reconciler/src/fiber.ts | 5 +- packages/react-reconciler/src/workLoop.ts | 4 +- packages/react/index.ts | 11 +- packages/react/src/jsx.ts | 8 + tsconfig.json | 1 + 15 files changed, 1782 insertions(+), 15 deletions(-) create mode 100644 dist/node_modules/react-dom/client.js create mode 100644 dist/node_modules/react-dom/index.js create mode 100644 dist/node_modules/react-dom/package.json create mode 100644 dist/node_modules/react/index.js create mode 100644 dist/node_modules/react/jsx-dev-runtime.js create mode 100644 dist/node_modules/react/jsx-runtime.js create mode 100644 dist/node_modules/react/package.json diff --git a/dist/node_modules/react-dom/client.js b/dist/node_modules/react-dom/client.js new file mode 100644 index 0000000..5f229c2 --- /dev/null +++ b/dist/node_modules/react-dom/client.js @@ -0,0 +1,699 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, (global.client = global.client || {}, global.client.js = factory())); +})(this, (function () { 'use strict'; + + /* + * @Author: chenjianfeng chenjianfeng93@163.com + * @Date: 2023-11-02 18:58:58 + * @Description: + */ + const FunctionComponent = 0; + // root + const HostRoot = 3; + //
-> div + const HostComponent = 5; + const HostText = 6; + + const NoFlags = 0b0000000; + const Placement = 0b0000001; + const Update = 0b0000010; + const ChildDeletion = 0b0000100; + const MutationMask = Placement | Update | ChildDeletion; + + /* + * @Author: chenjianfeng chenjianfeng93@163.com + * @Date: 2023-11-02 18:56:36 + * @Description: fiber structure + */ + class FiberNode { + type; + key; + tag; + stateNode; + pendingProps; + memorizedProps; + memorizedState; + updateQueue; + return; + sibling; + child; + index; + ref; + alternate; + flags; + subtreeFlags; + constructor(tag, pendingProps, key) { + this.key = key; + this.tag = tag; + this.stateNode = null; // store real dom + this.type = null; + this.return = null; + this.sibling = null; + this.child = null; + this.index = 0; // childNodes index + this.ref = null; + this.pendingProps = pendingProps; + this.memorizedProps = null; + this.updateQueue = null; + this.memorizedState = null; + this.alternate = null; + // effect + this.flags = NoFlags; + this.subtreeFlags = NoFlags; + } + } + class FiberRootNode { + container; // base on different environment + current; // FiberRootNode.current -> hostRootFiber hostRootFiber.stateNode -> FiberRootNode + finishedWork; + constructor(container, hostRootFiber) { + this.container = container; + this.current = hostRootFiber; + hostRootFiber.stateNode = this; + this.finishedWork = null; + } + } + const crateWorkInProgress = (current, pendingProps) => { + let wip = current.alternate; + if (wip === null) { + // mount + wip = new FiberNode(current.tag, pendingProps, current.key); + wip.stateNode = current.stateNode; + wip.alternate = current; + current.alternate = wip; + } + else { + // update + wip.pendingProps = pendingProps; + // reset effect + wip.flags = NoFlags; + wip.subtreeFlags = NoFlags; + } + wip.type = current.type; + wip.updateQueue = current.updateQueue; + wip.child = current.child; + wip.memorizedProps = current.memorizedProps; + wip.memorizedState = current.memorizedState; + return wip; + }; + const createFiberFromElement = (element) => { + const { type, key, props } = element; + let fiberTag = FunctionComponent; + if (typeof type === 'string') { + // div + fiberTag = HostComponent; + } + else if (typeof type !== 'function' && true) { + console.warn('unknown type', element); + } + const fiber = new FiberNode(fiberTag, props, key); + fiber.type = type; + return fiber; + }; + + const createUpdate = (action) => { + return { + action + }; + }; + const createUpdateQueue = () => { + return { + shared: { + pending: null + }, + dispatch: null + }; + }; + const enqueueUpdate = (updateQueue, update) => { + updateQueue.shared.pending = update; + }; + // consume update + const processUpdateQueue = (baseState, pendingUpdate) => { + const result = { + memorizedState: baseState + }; + if (pendingUpdate !== null) { + const action = pendingUpdate.action; + if (typeof action === 'function') { + result.memorizedState = action(baseState); + } + else { + result.memorizedState = action; + } + } + return result; + }; + + // environment supports symbol or not + const supportSymbol = typeof Symbol === 'function' && Symbol.for; + const REACT_ELEMENT_TYPE = supportSymbol + ? Symbol.for('React.element') + : 0xeac7; + + const ChildReconciler = (shouldTrackEffects) => { + const reconcileSingleElement = (returnFiber, currentFiber, element) => { + const fiber = createFiberFromElement(element); + fiber.return = returnFiber; + return fiber; + }; + const reconcileSingleTextNode = (returnFiber, currentFiber, content) => { + const fiber = new FiberNode(HostText, { content }, null); + fiber.return = returnFiber; + return fiber; + }; + const placeSingleChild = (fiber) => { + if (shouldTrackEffects && fiber.alternate === null) { + fiber.flags |= Placement; + } + return fiber; + }; + return function reconcileChildFibers(returnFiber, currentFiber, newChild) { + if (typeof newChild === 'object' && newChild !== null) { + switch (newChild.$$typeof) { + case REACT_ELEMENT_TYPE: + return placeSingleChild(reconcileSingleElement(returnFiber, currentFiber, newChild)); + default: + { + console.warn('unknown reconcile type'); + } + break; + } + } + if (typeof newChild === 'string' || typeof newChild === 'number') { + return placeSingleChild(reconcileSingleTextNode(returnFiber, currentFiber, newChild)); + } + { + console.warn('unknown reconcile type'); + } + return null; + }; + }; + const reconcileChildFibers = ChildReconciler(true); + const mountChildFibers = ChildReconciler(false); + + const currentDispatcher$1 = { + current: null + }; + const resolveDispatcher = () => { + const dispatcher = currentDispatcher$1.current; + if (dispatcher === null) { + throw new Error('hook only uses in function'); + } + return dispatcher; + }; + + const ReactElement = (type, key, ref, props) => { + const element = { + $$typeof: REACT_ELEMENT_TYPE, + type, + key, + ref, + props, + __marks: 'jeff' + }; + return element; + }; + const jsxDEV = (type, config) => { + const props = {}; + let key = null; + let ref = null; + // handle properties + for (const prop in config) { + const val = config[prop]; + if (prop === 'key') { + if (val !== undefined) { + key = val; + } + continue; + } + if (prop === 'ref') { + if (val !== undefined) { + ref = val; + } + continue; + } + if (Object.prototype.hasOwnProperty.call(config, prop)) { + props[prop] = val; + } + } + return ReactElement(type, key, ref, props); + }; + + /* + * @Author: chenjianfeng chenjianfeng93@163.com + * @Date: 2023-10-30 15:34:42 + * @Description: react entry + */ + const useState = (initialState) => { + const dispatcher = resolveDispatcher(); + return dispatcher.useState(initialState); + }; + const _Inner_data = { + currentDispatcher: currentDispatcher$1 + }; + const version = '0.0.0'; + const createElement = jsxDEV; + + var React = /*#__PURE__*/Object.freeze({ + __proto__: null, + _Inner_data: _Inner_data, + createElement: createElement, + useState: useState, + version: version + }); + + /* + * @Author: chenjianfeng chenjianfeng93@163.com + * @Date: 2023-11-11 16:14:37 + * @Description: + */ + console.log(React); + const internals = _Inner_data; + + let currentlyRenderFiber = null; + let workInProgressHook = null; + console.log('internals ->', internals); + const { currentDispatcher } = internals; + const renderWithHooks = (wip) => { + currentlyRenderFiber = wip; + wip.memorizedState = null; + const current = wip.alternate; + if (current !== null) ; + else { + // mount + currentDispatcher.current = HookDispatcherOnMount; + } + // FunctionComponent + const Component = wip.type; + const props = wip.pendingProps; + const children = Component(props); + currentlyRenderFiber = null; + return children; + }; + const mountState = (initialState) => { + const hook = mountWorkInProgressHook(); + let memorizedState; + if (initialState instanceof Function) { + memorizedState = initialState(); + } + else { + memorizedState = initialState; + } + const queue = createUpdateQueue(); + hook.updateQueue = queue; + // @ts-ignore + const dispatch = dispatchSetState.bind(null, currentlyRenderFiber, queue); + queue.dispatch = dispatch; + return [memorizedState, dispatch]; + }; + const dispatchSetState = (fiber, updateQueue, action) => { + const update = createUpdate(action); + enqueueUpdate(updateQueue, update); + scheduleUpdateOnFiber(fiber); + }; + const mountWorkInProgressHook = () => { + const hook = { + memorizedState: null, + updateQueue: null, + next: null + }; + if (workInProgressHook === null) { + if (currentlyRenderFiber === null) { + throw new Error('create hook error'); + } + else { + workInProgressHook = hook; + currentlyRenderFiber.memorizedState = workInProgressHook; + } + } + else { + workInProgressHook.next = hook; + workInProgressHook = hook; + } + return workInProgressHook; + }; + const HookDispatcherOnMount = { + useState: mountState + }; + + const beginWork = (wip) => { + switch (wip.tag) { + case HostRoot: + // 1. update new State + // 2. return child fiberNode + return updateHostRoot(wip); + case HostComponent: + return updateHostComponent(wip); + case HostText: + return null; + case FunctionComponent: + return updateFunctionComponent(wip); + default: + { + console.warn('unknown type by beginWork'); + } + } + return null; + }; + const updateHostRoot = (wip) => { + const baseState = wip.memorizedState; + const updateQueue = wip.updateQueue; + const pending = updateQueue.shared.pending; + updateQueue.shared.pending = null; + const { memorizedState } = processUpdateQueue(baseState, pending); + wip.memorizedState = memorizedState; + const nextChildren = wip.memorizedState; + // compare current child fiberNode with child reactElement -> (wip.memorizedState) + reconcileChildren(wip, nextChildren); + return wip.child; + }; + const updateFunctionComponent = (wip) => { + const nextChildren = renderWithHooks(wip); + reconcileChildren(wip, nextChildren); + return wip.child; + }; + //
-> div's children -> span + const updateHostComponent = (wip) => { + const props = wip.pendingProps; + const nextChildren = props.children; + reconcileChildren(wip, nextChildren); + return wip.child; + }; + const reconcileChildren = (wip, children) => { + const current = wip.alternate; + if (current !== null) { + // update + wip.child = reconcileChildFibers(wip, current?.child, children); + } + else { + // mount + wip.child = mountChildFibers(wip, null, children); + } + }; + + // export const createInstance = (type: string, props: any): Instance => { + const createInstance = (type) => { + const element = document.createElement(type); + return element; + }; + const appendInitialChild = (parent, child) => { + parent.appendChild(child); + }; + const createTextInstance = (content) => { + return document.createTextNode(content); + }; + const appendChildToContainer = appendInitialChild; + + const completeWork = (wip) => { + const newProps = wip.pendingProps; + const current = wip.alternate; + switch (wip.tag) { + case HostRoot: + bubbleProperties(wip); + return null; + case HostComponent: + if (current !== null && wip.stateNode) ; + else { + // 1. create Dom 2. insert dom + // const instance = createInstance(wip.type, newProps) + const instance = createInstance(wip.type); + appendAllChildren(instance, wip); + wip.stateNode = instance; + } + bubbleProperties(wip); + return null; + case HostText: + const instance = createTextInstance(newProps.content); + wip.stateNode = instance; + bubbleProperties(wip); + return null; + default: + { + console.warn('unknown completeWork'); + } + break; + } + }; + const appendAllChildren = (parent, wip) => { + let node = wip.child; + while (node !== null) { + if (node.tag === HostComponent || node.tag === HostText) { + appendInitialChild(parent, node?.stateNode); + } + else if (node.child !== null) { + node.child.return = node; + node = node.child; + continue; + } + if (node === wip) + return; + while (node.sibling === null) { + if (node.return === null || node.return === wip) { + return; + } + node = node.return; + } + node.sibling.return = node.return; + node = node.sibling; + } + }; + const bubbleProperties = (wip) => { + let subtreeFlags = NoFlags; + let child = wip.child; + while (child !== null) { + subtreeFlags |= child.subtreeFlags; + subtreeFlags |= child.flags; + child.return = wip; + child = child.sibling; + } + wip.subtreeFlags |= subtreeFlags; + }; + + /* + * @Author: chenjianfeng chenjianfeng93@163.com + * @Date: 2023-11-07 16:04:55 + * @Description: + */ + const commitMutationEffects = (finishedWork) => { + let nextEffect = finishedWork; + while (nextEffect !== null) { + const child = nextEffect.child; + if ((nextEffect.subtreeFlags & MutationMask) !== NoFlags && + child !== null) { + nextEffect = child; + } + else { + up: while (nextEffect !== null) { + commitMutationEffectsOnFiber(nextEffect); + const sibling = nextEffect.sibling; + if (sibling !== null) { + nextEffect = sibling; + break up; + } + nextEffect = nextEffect.return; + } + } + } + }; + const commitMutationEffectsOnFiber = (finishedWork) => { + const flags = finishedWork.flags; + if ((flags & Placement) !== NoFlags) { + commitPlacement(finishedWork); + // remove Placement from flags + finishedWork.flags &= ~Placement; + } + // update + // childDelection + }; + const commitPlacement = (finishedWork) => { + // insert dom to parent's dom + { + console.warn('execute Placement operation'); + } + const hostParent = getHostParent(finishedWork); + if (hostParent) { + appendPlacementNodeIntoContainer(finishedWork, hostParent); + } + }; + const getHostParent = (fiber) => { + let parent = fiber.return; + while (parent) { + const parentTag = parent.tag; + if (parentTag === HostComponent) { + return parent.stateNode; + } + if (parentTag === HostRoot) { + return parent.stateNode.container; + } + parent = parent.return; + } + { + console.warn('can not find host parent'); + } + return null; + }; + const appendPlacementNodeIntoContainer = (finishedWork, hostParent) => { + if (finishedWork.tag === HostComponent || finishedWork.tag === HostText) { + appendChildToContainer(hostParent, finishedWork.stateNode); + return; + } + const child = finishedWork.child; + if (child !== null) { + appendPlacementNodeIntoContainer(child, hostParent); + let sibling = child.sibling; + while (sibling !== null) { + appendPlacementNodeIntoContainer(sibling, hostParent); + sibling = sibling.sibling; + } + } + }; + + /* + * @Author: chenjianfeng chenjianfeng93@163.com + * @Date: 2023-11-02 19:42:15 + * @Description: + */ + let workInProgress = null; + const prepareFreshStack = (root) => { + workInProgress = crateWorkInProgress(root.current, {}); + }; + const scheduleUpdateOnFiber = (fiber) => { + const root = markUpdateFromFiberToRoot(fiber); + renderRoot(root); + }; + const markUpdateFromFiberToRoot = (fiber) => { + let node = fiber; + let parent = node.return; + while (parent !== null) { + node = parent; + parent = node.return; + } + if (node.tag === HostRoot) { + return node.stateNode; + } + return null; + }; + const renderRoot = (root) => { + // initialize workInProgress + prepareFreshStack(root); + do { + try { + workLoop(); + break; + } + catch (_err) { + { + console.warn('workLoop发生错误'); + } + workInProgress = null; + } + } while (true); + const finishedWork = root.current.alternate; + // finishedWork is wip + root.finishedWork = finishedWork; + commitRoot(root); + }; + const commitRoot = (root) => { + // 1. switch fiber tree + // 2. execute Placement operation + const finishedWork = root.finishedWork; + if (finishedWork === null) + return; + console.warn('commit start', finishedWork); + // reset + root.finishedWork = null; + const subtreeHasEffect = (finishedWork.subtreeFlags & MutationMask) !== NoFlags; + const rootHasEffect = (finishedWork.flags & MutationMask) !== NoFlags; + if (subtreeHasEffect || rootHasEffect) { + // beforeMutation + // mutation + // switch fiber tree + // hostRootNode + commitMutationEffects(finishedWork); + root.current = finishedWork; + // layout + } + else { + root.current = finishedWork; + } + }; + const workLoop = () => { + while (workInProgress !== null) { + performUnitOfWork(workInProgress); + } + }; + const performUnitOfWork = (fiber) => { + const next = beginWork(fiber); + fiber.memorizedProps = fiber.pendingProps; + if (next === null) { + completeUnitOfWork(fiber); + } + else { + workInProgress = next; + } + }; + const completeUnitOfWork = (fiber) => { + let node = fiber; + do { + completeWork(node); + const sibling = node.sibling; + if (sibling !== null) { + workInProgress = sibling; + return; + } + node = node.return; + workInProgress = node; + } while (node !== null); + }; + + /* + * @Author: chenjianfeng chenjianfeng93@163.com + * @Date: 2023-11-03 15:22:05 + * @Description: + */ + // ReactDom.createRoot + const createContainer = (container) => { + const hostRootFiber = new FiberNode(HostRoot, {}, null); + const root = new FiberRootNode(container, hostRootFiber); + hostRootFiber.updateQueue = createUpdateQueue(); + return root; + }; + // ReactDom.createRoot().render + const updateContainer = (element, root) => { + const hostRootFiber = root.current; + const update = createUpdate(element); + enqueueUpdate(hostRootFiber.updateQueue, update); + scheduleUpdateOnFiber(hostRootFiber); + return element; + }; + + /* + * @Author: chenjianfeng chenjianfeng93@163.com + * @Date: 2023-11-07 22:25:43 + * @Description: + */ + const createRoot = (container) => { + const root = createContainer(container); + return { + render(element) { + return updateContainer(element, root); + } + }; + }; + + var ReactDOM = /*#__PURE__*/Object.freeze({ + __proto__: null, + createRoot: createRoot + }); + + /* + * @Author: chenjianfeng chenjianfeng93@163.com + * @Date: 2023-11-07 23:33:32 + * @Description: + */ + + return ReactDOM; + +})); diff --git a/dist/node_modules/react-dom/index.js b/dist/node_modules/react-dom/index.js new file mode 100644 index 0000000..95b43b9 --- /dev/null +++ b/dist/node_modules/react-dom/index.js @@ -0,0 +1,699 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, (global.index = global.index || {}, global.index.js = factory())); +})(this, (function () { 'use strict'; + + /* + * @Author: chenjianfeng chenjianfeng93@163.com + * @Date: 2023-11-02 18:58:58 + * @Description: + */ + const FunctionComponent = 0; + // root + const HostRoot = 3; + //
-> div + const HostComponent = 5; + const HostText = 6; + + const NoFlags = 0b0000000; + const Placement = 0b0000001; + const Update = 0b0000010; + const ChildDeletion = 0b0000100; + const MutationMask = Placement | Update | ChildDeletion; + + /* + * @Author: chenjianfeng chenjianfeng93@163.com + * @Date: 2023-11-02 18:56:36 + * @Description: fiber structure + */ + class FiberNode { + type; + key; + tag; + stateNode; + pendingProps; + memorizedProps; + memorizedState; + updateQueue; + return; + sibling; + child; + index; + ref; + alternate; + flags; + subtreeFlags; + constructor(tag, pendingProps, key) { + this.key = key; + this.tag = tag; + this.stateNode = null; // store real dom + this.type = null; + this.return = null; + this.sibling = null; + this.child = null; + this.index = 0; // childNodes index + this.ref = null; + this.pendingProps = pendingProps; + this.memorizedProps = null; + this.updateQueue = null; + this.memorizedState = null; + this.alternate = null; + // effect + this.flags = NoFlags; + this.subtreeFlags = NoFlags; + } + } + class FiberRootNode { + container; // base on different environment + current; // FiberRootNode.current -> hostRootFiber hostRootFiber.stateNode -> FiberRootNode + finishedWork; + constructor(container, hostRootFiber) { + this.container = container; + this.current = hostRootFiber; + hostRootFiber.stateNode = this; + this.finishedWork = null; + } + } + const crateWorkInProgress = (current, pendingProps) => { + let wip = current.alternate; + if (wip === null) { + // mount + wip = new FiberNode(current.tag, pendingProps, current.key); + wip.stateNode = current.stateNode; + wip.alternate = current; + current.alternate = wip; + } + else { + // update + wip.pendingProps = pendingProps; + // reset effect + wip.flags = NoFlags; + wip.subtreeFlags = NoFlags; + } + wip.type = current.type; + wip.updateQueue = current.updateQueue; + wip.child = current.child; + wip.memorizedProps = current.memorizedProps; + wip.memorizedState = current.memorizedState; + return wip; + }; + const createFiberFromElement = (element) => { + const { type, key, props } = element; + let fiberTag = FunctionComponent; + if (typeof type === 'string') { + // div + fiberTag = HostComponent; + } + else if (typeof type !== 'function' && true) { + console.warn('unknown type', element); + } + const fiber = new FiberNode(fiberTag, props, key); + fiber.type = type; + return fiber; + }; + + const createUpdate = (action) => { + return { + action + }; + }; + const createUpdateQueue = () => { + return { + shared: { + pending: null + }, + dispatch: null + }; + }; + const enqueueUpdate = (updateQueue, update) => { + updateQueue.shared.pending = update; + }; + // consume update + const processUpdateQueue = (baseState, pendingUpdate) => { + const result = { + memorizedState: baseState + }; + if (pendingUpdate !== null) { + const action = pendingUpdate.action; + if (typeof action === 'function') { + result.memorizedState = action(baseState); + } + else { + result.memorizedState = action; + } + } + return result; + }; + + // environment supports symbol or not + const supportSymbol = typeof Symbol === 'function' && Symbol.for; + const REACT_ELEMENT_TYPE = supportSymbol + ? Symbol.for('React.element') + : 0xeac7; + + const ChildReconciler = (shouldTrackEffects) => { + const reconcileSingleElement = (returnFiber, currentFiber, element) => { + const fiber = createFiberFromElement(element); + fiber.return = returnFiber; + return fiber; + }; + const reconcileSingleTextNode = (returnFiber, currentFiber, content) => { + const fiber = new FiberNode(HostText, { content }, null); + fiber.return = returnFiber; + return fiber; + }; + const placeSingleChild = (fiber) => { + if (shouldTrackEffects && fiber.alternate === null) { + fiber.flags |= Placement; + } + return fiber; + }; + return function reconcileChildFibers(returnFiber, currentFiber, newChild) { + if (typeof newChild === 'object' && newChild !== null) { + switch (newChild.$$typeof) { + case REACT_ELEMENT_TYPE: + return placeSingleChild(reconcileSingleElement(returnFiber, currentFiber, newChild)); + default: + { + console.warn('unknown reconcile type'); + } + break; + } + } + if (typeof newChild === 'string' || typeof newChild === 'number') { + return placeSingleChild(reconcileSingleTextNode(returnFiber, currentFiber, newChild)); + } + { + console.warn('unknown reconcile type'); + } + return null; + }; + }; + const reconcileChildFibers = ChildReconciler(true); + const mountChildFibers = ChildReconciler(false); + + const currentDispatcher$1 = { + current: null + }; + const resolveDispatcher = () => { + const dispatcher = currentDispatcher$1.current; + if (dispatcher === null) { + throw new Error('hook only uses in function'); + } + return dispatcher; + }; + + const ReactElement = (type, key, ref, props) => { + const element = { + $$typeof: REACT_ELEMENT_TYPE, + type, + key, + ref, + props, + __marks: 'jeff' + }; + return element; + }; + const jsxDEV = (type, config) => { + const props = {}; + let key = null; + let ref = null; + // handle properties + for (const prop in config) { + const val = config[prop]; + if (prop === 'key') { + if (val !== undefined) { + key = val; + } + continue; + } + if (prop === 'ref') { + if (val !== undefined) { + ref = val; + } + continue; + } + if (Object.prototype.hasOwnProperty.call(config, prop)) { + props[prop] = val; + } + } + return ReactElement(type, key, ref, props); + }; + + /* + * @Author: chenjianfeng chenjianfeng93@163.com + * @Date: 2023-10-30 15:34:42 + * @Description: react entry + */ + const useState = (initialState) => { + const dispatcher = resolveDispatcher(); + return dispatcher.useState(initialState); + }; + const _Inner_data = { + currentDispatcher: currentDispatcher$1 + }; + const version = '0.0.0'; + const createElement = jsxDEV; + + var React = /*#__PURE__*/Object.freeze({ + __proto__: null, + _Inner_data: _Inner_data, + createElement: createElement, + useState: useState, + version: version + }); + + /* + * @Author: chenjianfeng chenjianfeng93@163.com + * @Date: 2023-11-11 16:14:37 + * @Description: + */ + console.log(React); + const internals = _Inner_data; + + let currentlyRenderFiber = null; + let workInProgressHook = null; + console.log('internals ->', internals); + const { currentDispatcher } = internals; + const renderWithHooks = (wip) => { + currentlyRenderFiber = wip; + wip.memorizedState = null; + const current = wip.alternate; + if (current !== null) ; + else { + // mount + currentDispatcher.current = HookDispatcherOnMount; + } + // FunctionComponent + const Component = wip.type; + const props = wip.pendingProps; + const children = Component(props); + currentlyRenderFiber = null; + return children; + }; + const mountState = (initialState) => { + const hook = mountWorkInProgressHook(); + let memorizedState; + if (initialState instanceof Function) { + memorizedState = initialState(); + } + else { + memorizedState = initialState; + } + const queue = createUpdateQueue(); + hook.updateQueue = queue; + // @ts-ignore + const dispatch = dispatchSetState.bind(null, currentlyRenderFiber, queue); + queue.dispatch = dispatch; + return [memorizedState, dispatch]; + }; + const dispatchSetState = (fiber, updateQueue, action) => { + const update = createUpdate(action); + enqueueUpdate(updateQueue, update); + scheduleUpdateOnFiber(fiber); + }; + const mountWorkInProgressHook = () => { + const hook = { + memorizedState: null, + updateQueue: null, + next: null + }; + if (workInProgressHook === null) { + if (currentlyRenderFiber === null) { + throw new Error('create hook error'); + } + else { + workInProgressHook = hook; + currentlyRenderFiber.memorizedState = workInProgressHook; + } + } + else { + workInProgressHook.next = hook; + workInProgressHook = hook; + } + return workInProgressHook; + }; + const HookDispatcherOnMount = { + useState: mountState + }; + + const beginWork = (wip) => { + switch (wip.tag) { + case HostRoot: + // 1. update new State + // 2. return child fiberNode + return updateHostRoot(wip); + case HostComponent: + return updateHostComponent(wip); + case HostText: + return null; + case FunctionComponent: + return updateFunctionComponent(wip); + default: + { + console.warn('unknown type by beginWork'); + } + } + return null; + }; + const updateHostRoot = (wip) => { + const baseState = wip.memorizedState; + const updateQueue = wip.updateQueue; + const pending = updateQueue.shared.pending; + updateQueue.shared.pending = null; + const { memorizedState } = processUpdateQueue(baseState, pending); + wip.memorizedState = memorizedState; + const nextChildren = wip.memorizedState; + // compare current child fiberNode with child reactElement -> (wip.memorizedState) + reconcileChildren(wip, nextChildren); + return wip.child; + }; + const updateFunctionComponent = (wip) => { + const nextChildren = renderWithHooks(wip); + reconcileChildren(wip, nextChildren); + return wip.child; + }; + //
-> div's children -> span + const updateHostComponent = (wip) => { + const props = wip.pendingProps; + const nextChildren = props.children; + reconcileChildren(wip, nextChildren); + return wip.child; + }; + const reconcileChildren = (wip, children) => { + const current = wip.alternate; + if (current !== null) { + // update + wip.child = reconcileChildFibers(wip, current?.child, children); + } + else { + // mount + wip.child = mountChildFibers(wip, null, children); + } + }; + + // export const createInstance = (type: string, props: any): Instance => { + const createInstance = (type) => { + const element = document.createElement(type); + return element; + }; + const appendInitialChild = (parent, child) => { + parent.appendChild(child); + }; + const createTextInstance = (content) => { + return document.createTextNode(content); + }; + const appendChildToContainer = appendInitialChild; + + const completeWork = (wip) => { + const newProps = wip.pendingProps; + const current = wip.alternate; + switch (wip.tag) { + case HostRoot: + bubbleProperties(wip); + return null; + case HostComponent: + if (current !== null && wip.stateNode) ; + else { + // 1. create Dom 2. insert dom + // const instance = createInstance(wip.type, newProps) + const instance = createInstance(wip.type); + appendAllChildren(instance, wip); + wip.stateNode = instance; + } + bubbleProperties(wip); + return null; + case HostText: + const instance = createTextInstance(newProps.content); + wip.stateNode = instance; + bubbleProperties(wip); + return null; + default: + { + console.warn('unknown completeWork'); + } + break; + } + }; + const appendAllChildren = (parent, wip) => { + let node = wip.child; + while (node !== null) { + if (node.tag === HostComponent || node.tag === HostText) { + appendInitialChild(parent, node?.stateNode); + } + else if (node.child !== null) { + node.child.return = node; + node = node.child; + continue; + } + if (node === wip) + return; + while (node.sibling === null) { + if (node.return === null || node.return === wip) { + return; + } + node = node.return; + } + node.sibling.return = node.return; + node = node.sibling; + } + }; + const bubbleProperties = (wip) => { + let subtreeFlags = NoFlags; + let child = wip.child; + while (child !== null) { + subtreeFlags |= child.subtreeFlags; + subtreeFlags |= child.flags; + child.return = wip; + child = child.sibling; + } + wip.subtreeFlags |= subtreeFlags; + }; + + /* + * @Author: chenjianfeng chenjianfeng93@163.com + * @Date: 2023-11-07 16:04:55 + * @Description: + */ + const commitMutationEffects = (finishedWork) => { + let nextEffect = finishedWork; + while (nextEffect !== null) { + const child = nextEffect.child; + if ((nextEffect.subtreeFlags & MutationMask) !== NoFlags && + child !== null) { + nextEffect = child; + } + else { + up: while (nextEffect !== null) { + commitMutationEffectsOnFiber(nextEffect); + const sibling = nextEffect.sibling; + if (sibling !== null) { + nextEffect = sibling; + break up; + } + nextEffect = nextEffect.return; + } + } + } + }; + const commitMutationEffectsOnFiber = (finishedWork) => { + const flags = finishedWork.flags; + if ((flags & Placement) !== NoFlags) { + commitPlacement(finishedWork); + // remove Placement from flags + finishedWork.flags &= ~Placement; + } + // update + // childDelection + }; + const commitPlacement = (finishedWork) => { + // insert dom to parent's dom + { + console.warn('execute Placement operation'); + } + const hostParent = getHostParent(finishedWork); + if (hostParent) { + appendPlacementNodeIntoContainer(finishedWork, hostParent); + } + }; + const getHostParent = (fiber) => { + let parent = fiber.return; + while (parent) { + const parentTag = parent.tag; + if (parentTag === HostComponent) { + return parent.stateNode; + } + if (parentTag === HostRoot) { + return parent.stateNode.container; + } + parent = parent.return; + } + { + console.warn('can not find host parent'); + } + return null; + }; + const appendPlacementNodeIntoContainer = (finishedWork, hostParent) => { + if (finishedWork.tag === HostComponent || finishedWork.tag === HostText) { + appendChildToContainer(hostParent, finishedWork.stateNode); + return; + } + const child = finishedWork.child; + if (child !== null) { + appendPlacementNodeIntoContainer(child, hostParent); + let sibling = child.sibling; + while (sibling !== null) { + appendPlacementNodeIntoContainer(sibling, hostParent); + sibling = sibling.sibling; + } + } + }; + + /* + * @Author: chenjianfeng chenjianfeng93@163.com + * @Date: 2023-11-02 19:42:15 + * @Description: + */ + let workInProgress = null; + const prepareFreshStack = (root) => { + workInProgress = crateWorkInProgress(root.current, {}); + }; + const scheduleUpdateOnFiber = (fiber) => { + const root = markUpdateFromFiberToRoot(fiber); + renderRoot(root); + }; + const markUpdateFromFiberToRoot = (fiber) => { + let node = fiber; + let parent = node.return; + while (parent !== null) { + node = parent; + parent = node.return; + } + if (node.tag === HostRoot) { + return node.stateNode; + } + return null; + }; + const renderRoot = (root) => { + // initialize workInProgress + prepareFreshStack(root); + do { + try { + workLoop(); + break; + } + catch (_err) { + { + console.warn('workLoop发生错误'); + } + workInProgress = null; + } + } while (true); + const finishedWork = root.current.alternate; + // finishedWork is wip + root.finishedWork = finishedWork; + commitRoot(root); + }; + const commitRoot = (root) => { + // 1. switch fiber tree + // 2. execute Placement operation + const finishedWork = root.finishedWork; + if (finishedWork === null) + return; + console.warn('commit start', finishedWork); + // reset + root.finishedWork = null; + const subtreeHasEffect = (finishedWork.subtreeFlags & MutationMask) !== NoFlags; + const rootHasEffect = (finishedWork.flags & MutationMask) !== NoFlags; + if (subtreeHasEffect || rootHasEffect) { + // beforeMutation + // mutation + // switch fiber tree + // hostRootNode + commitMutationEffects(finishedWork); + root.current = finishedWork; + // layout + } + else { + root.current = finishedWork; + } + }; + const workLoop = () => { + while (workInProgress !== null) { + performUnitOfWork(workInProgress); + } + }; + const performUnitOfWork = (fiber) => { + const next = beginWork(fiber); + fiber.memorizedProps = fiber.pendingProps; + if (next === null) { + completeUnitOfWork(fiber); + } + else { + workInProgress = next; + } + }; + const completeUnitOfWork = (fiber) => { + let node = fiber; + do { + completeWork(node); + const sibling = node.sibling; + if (sibling !== null) { + workInProgress = sibling; + return; + } + node = node.return; + workInProgress = node; + } while (node !== null); + }; + + /* + * @Author: chenjianfeng chenjianfeng93@163.com + * @Date: 2023-11-03 15:22:05 + * @Description: + */ + // ReactDom.createRoot + const createContainer = (container) => { + const hostRootFiber = new FiberNode(HostRoot, {}, null); + const root = new FiberRootNode(container, hostRootFiber); + hostRootFiber.updateQueue = createUpdateQueue(); + return root; + }; + // ReactDom.createRoot().render + const updateContainer = (element, root) => { + const hostRootFiber = root.current; + const update = createUpdate(element); + enqueueUpdate(hostRootFiber.updateQueue, update); + scheduleUpdateOnFiber(hostRootFiber); + return element; + }; + + /* + * @Author: chenjianfeng chenjianfeng93@163.com + * @Date: 2023-11-07 22:25:43 + * @Description: + */ + const createRoot = (container) => { + const root = createContainer(container); + return { + render(element) { + return updateContainer(element, root); + } + }; + }; + + var ReactDOM = /*#__PURE__*/Object.freeze({ + __proto__: null, + createRoot: createRoot + }); + + /* + * @Author: chenjianfeng chenjianfeng93@163.com + * @Date: 2023-11-07 23:33:32 + * @Description: + */ + + return ReactDOM; + +})); diff --git a/dist/node_modules/react-dom/package.json b/dist/node_modules/react-dom/package.json new file mode 100644 index 0000000..11f3baa --- /dev/null +++ b/dist/node_modules/react-dom/package.json @@ -0,0 +1,9 @@ +{ + "name": "react-dom", + "description": "", + "version": "1.0.0", + "peerDependencies": { + "react": "1.0.0" + }, + "main": "index.js" +} diff --git a/dist/node_modules/react/index.js b/dist/node_modules/react/index.js new file mode 100644 index 0000000..5716c83 --- /dev/null +++ b/dist/node_modules/react/index.js @@ -0,0 +1,81 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.index = global.index || {}, global.index.js = {}))); +})(this, (function (exports) { 'use strict'; + + const currentDispatcher = { + current: null + }; + const resolveDispatcher = () => { + const dispatcher = currentDispatcher.current; + if (dispatcher === null) { + throw new Error('hook only uses in function'); + } + return dispatcher; + }; + + // environment supports symbol or not + const supportSymbol = typeof Symbol === 'function' && Symbol.for; + const REACT_ELEMENT_TYPE = supportSymbol + ? Symbol.for('React.element') + : 0xeac7; + + const ReactElement = (type, key, ref, props) => { + const element = { + $$typeof: REACT_ELEMENT_TYPE, + type, + key, + ref, + props, + __marks: 'jeff' + }; + return element; + }; + const jsxDEV = (type, config) => { + const props = {}; + let key = null; + let ref = null; + // handle properties + for (const prop in config) { + const val = config[prop]; + if (prop === 'key') { + if (val !== undefined) { + key = val; + } + continue; + } + if (prop === 'ref') { + if (val !== undefined) { + ref = val; + } + continue; + } + if (Object.prototype.hasOwnProperty.call(config, prop)) { + props[prop] = val; + } + } + return ReactElement(type, key, ref, props); + }; + + /* + * @Author: chenjianfeng chenjianfeng93@163.com + * @Date: 2023-10-30 15:34:42 + * @Description: react entry + */ + const useState = (initialState) => { + const dispatcher = resolveDispatcher(); + return dispatcher.useState(initialState); + }; + const _Inner_data = { + currentDispatcher + }; + const version = '0.0.0'; + const createElement = jsxDEV; + + exports._Inner_data = _Inner_data; + exports.createElement = createElement; + exports.useState = useState; + exports.version = version; + +})); diff --git a/dist/node_modules/react/jsx-dev-runtime.js b/dist/node_modules/react/jsx-dev-runtime.js new file mode 100644 index 0000000..1300ae5 --- /dev/null +++ b/dist/node_modules/react/jsx-dev-runtime.js @@ -0,0 +1,90 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global["jsx-dev-runtime"] = global["jsx-dev-runtime"] || {}, global["jsx-dev-runtime"].js = {}))); +})(this, (function (exports) { 'use strict'; + + // environment supports symbol or not + const supportSymbol = typeof Symbol === 'function' && Symbol.for; + const REACT_ELEMENT_TYPE = supportSymbol + ? Symbol.for('React.element') + : 0xeac7; + + const ReactElement = (type, key, ref, props) => { + const element = { + $$typeof: REACT_ELEMENT_TYPE, + type, + key, + ref, + props, + __marks: 'jeff' + }; + return element; + }; + const jsx = (type, config, ...maybeChildren) => { + const props = {}; + let key = null; + let ref = null; + // handle properties + for (const prop in config) { + const val = config[prop]; + if (prop === 'key') { + if (val !== undefined) { + key = val; + } + continue; + } + if (prop === 'ref') { + if (val !== undefined) { + ref = val; + } + continue; + } + if (Object.prototype.hasOwnProperty.call(config, prop)) { + props[prop] = val; + } + } + handleChildren(maybeChildren, props); + return ReactElement(type, key, ref, props); + }; + const handleChildren = (children, props) => { + const maybeChildrenLength = children.length; + if (maybeChildrenLength) { + if (maybeChildrenLength === 1) { + props.children = children[0]; + } + else { + props.children = children; + } + } + }; + const jsxDEV = (type, config) => { + const props = {}; + let key = null; + let ref = null; + // handle properties + for (const prop in config) { + const val = config[prop]; + if (prop === 'key') { + if (val !== undefined) { + key = val; + } + continue; + } + if (prop === 'ref') { + if (val !== undefined) { + ref = val; + } + continue; + } + if (Object.prototype.hasOwnProperty.call(config, prop)) { + props[prop] = val; + } + } + return ReactElement(type, key, ref, props); + }; + + exports.jsx = jsx; + exports.jsxDEV = jsxDEV; + +})); diff --git a/dist/node_modules/react/jsx-runtime.js b/dist/node_modules/react/jsx-runtime.js new file mode 100644 index 0000000..0099cc0 --- /dev/null +++ b/dist/node_modules/react/jsx-runtime.js @@ -0,0 +1,90 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global["jsx-runtime"] = global["jsx-runtime"] || {}, global["jsx-runtime"].js = {}))); +})(this, (function (exports) { 'use strict'; + + // environment supports symbol or not + const supportSymbol = typeof Symbol === 'function' && Symbol.for; + const REACT_ELEMENT_TYPE = supportSymbol + ? Symbol.for('React.element') + : 0xeac7; + + const ReactElement = (type, key, ref, props) => { + const element = { + $$typeof: REACT_ELEMENT_TYPE, + type, + key, + ref, + props, + __marks: 'jeff' + }; + return element; + }; + const jsx = (type, config, ...maybeChildren) => { + const props = {}; + let key = null; + let ref = null; + // handle properties + for (const prop in config) { + const val = config[prop]; + if (prop === 'key') { + if (val !== undefined) { + key = val; + } + continue; + } + if (prop === 'ref') { + if (val !== undefined) { + ref = val; + } + continue; + } + if (Object.prototype.hasOwnProperty.call(config, prop)) { + props[prop] = val; + } + } + handleChildren(maybeChildren, props); + return ReactElement(type, key, ref, props); + }; + const handleChildren = (children, props) => { + const maybeChildrenLength = children.length; + if (maybeChildrenLength) { + if (maybeChildrenLength === 1) { + props.children = children[0]; + } + else { + props.children = children; + } + } + }; + const jsxDEV = (type, config) => { + const props = {}; + let key = null; + let ref = null; + // handle properties + for (const prop in config) { + const val = config[prop]; + if (prop === 'key') { + if (val !== undefined) { + key = val; + } + continue; + } + if (prop === 'ref') { + if (val !== undefined) { + ref = val; + } + continue; + } + if (Object.prototype.hasOwnProperty.call(config, prop)) { + props[prop] = val; + } + } + return ReactElement(type, key, ref, props); + }; + + exports.jsx = jsx; + exports.jsxDEV = jsxDEV; + +})); diff --git a/dist/node_modules/react/package.json b/dist/node_modules/react/package.json new file mode 100644 index 0000000..fd6d404 --- /dev/null +++ b/dist/node_modules/react/package.json @@ -0,0 +1,6 @@ +{ + "name": "react", + "description": "", + "version": "1.0.0", + "main": "index.js" +} diff --git a/packages/react-dom/src/root.ts b/packages/react-dom/src/root.ts index f49592d..40a6756 100644 --- a/packages/react-dom/src/root.ts +++ b/packages/react-dom/src/root.ts @@ -15,7 +15,7 @@ export const createRoot = (container: Container) => { return { render(element: ReactElementType) { - updateContainer(element, root) + return updateContainer(element, root) } } } diff --git a/packages/react-reconciler/src/childFiber.ts b/packages/react-reconciler/src/childFiber.ts index abd267d..ae770e1 100644 --- a/packages/react-reconciler/src/childFiber.ts +++ b/packages/react-reconciler/src/childFiber.ts @@ -3,18 +3,59 @@ * @Date: 2023-11-05 13:47:45 * @Description: */ -import { ReactElementType } from 'shared/ReactTypes' -import { FiberNode, createFiberFromElement } from './fiber' +import { ReactElementType, Props } from 'shared/ReactTypes' +import { + FiberNode, + createFiberFromElement, + createWorkInProgress +} from './fiber' import { REACT_ELEMENT_TYPE } from 'shared/ReactSymbols' import { HostText } from './workTags' -import { Placement } from './fiberFlags' +import { ChildDeletion, Placement } from './fiberFlags' +// import ReactDOM from 'react-dom' const ChildReconciler = (shouldTrackEffects: boolean) => { + const deleteChild = (returnFiber: FiberNode, childToDelete: FiberNode) => { + if (!shouldTrackEffects) return + const deletions = returnFiber.deletions + if (deletions === null) { + returnFiber.deletions = [childToDelete] + returnFiber.flags |= ChildDeletion + } else { + deletions.push(childToDelete) + } + } const reconcileSingleElement = ( returnFiber: FiberNode, currentFiber: FiberNode | null, element: ReactElementType ) => { + const key = element.key + work: if (currentFiber !== null) { + // update + if (currentFiber.key === key) { + if (element.$$typeof === REACT_ELEMENT_TYPE) { + if (currentFiber.type === element.type) { + // equal + // change to the wip + const existing = useFiber(currentFiber, element.props) + existing.return = returnFiber + return existing + } + deleteChild(returnFiber, currentFiber) + break work + } else { + if (__DEV__) { + console.warn('unknown react type', element) + break work + } + } + } else { + // delete + deleteChild(returnFiber, currentFiber) + } + } + const fiber = createFiberFromElement(element) fiber.return = returnFiber @@ -27,6 +68,17 @@ const ChildReconciler = (shouldTrackEffects: boolean) => { currentFiber: FiberNode | null, content: string | number ) => { + if (currentFiber !== null) { + // update + if (currentFiber.tag === HostText) { + const existing = useFiber(currentFiber, { content }) + existing.return = returnFiber + return existing + } + //
-> abc + deleteChild(returnFiber, currentFiber) + } + const fiber = new FiberNode(HostText, { content }, null) fiber.return = returnFiber @@ -65,6 +117,10 @@ const ChildReconciler = (shouldTrackEffects: boolean) => { ) } + if (currentFiber !== null) { + deleteChild(returnFiber, currentFiber) + } + if (__DEV__) { console.warn('unknown reconcile type') } @@ -72,5 +128,12 @@ const ChildReconciler = (shouldTrackEffects: boolean) => { } } +const useFiber = (fiber: FiberNode, pendingProps: Props): FiberNode => { + const clone = createWorkInProgress(fiber, pendingProps) + clone.index = 0 + clone.sibling = null + return clone +} + export const reconcileChildFibers = ChildReconciler(true) export const mountChildFibers = ChildReconciler(false) diff --git a/packages/react-reconciler/src/completeWork.ts b/packages/react-reconciler/src/completeWork.ts index dc16465..ee3e016 100644 --- a/packages/react-reconciler/src/completeWork.ts +++ b/packages/react-reconciler/src/completeWork.ts @@ -1,3 +1,8 @@ +/* + * @Author: chenjianfeng chenjianfeng93@163.com + * @Date: 2023-11-02 19:38:35 + * @Description: + */ import { createInstance, appendInitialChild, @@ -6,7 +11,11 @@ import { } from 'hostConfig' import { FiberNode } from './fiber' import { HostComponent, HostRoot, HostText } from './workTags' -import { NoFlags } from './fiberFlags' +import { NoFlags, Update } from './fiberFlags' + +const markUpdate = (fiber: FiberNode) => { + fiber.flags |= Update +} export const completeWork = (wip: FiberNode) => { const newProps = wip.pendingProps @@ -29,8 +38,16 @@ export const completeWork = (wip: FiberNode) => { bubbleProperties(wip) return null case HostText: - const instance = createTextInstance(newProps.content) - wip.stateNode = instance + if (current !== null && wip.stateNode) { + const oldText = current.memorizedProps.content + const newText = newProps.content + if (oldText !== newText) { + markUpdate(wip) + } + } else { + const instance = createTextInstance(newProps.content) + wip.stateNode = instance + } bubbleProperties(wip) return null default: diff --git a/packages/react-reconciler/src/fiber.ts b/packages/react-reconciler/src/fiber.ts index a576260..7cb3e2b 100644 --- a/packages/react-reconciler/src/fiber.ts +++ b/packages/react-reconciler/src/fiber.ts @@ -18,6 +18,7 @@ export class FiberNode { memorizedProps: Props | null memorizedState: any updateQueue: unknown + deletions: FiberNode[] | null return: FiberNode | null sibling: FiberNode | null @@ -47,6 +48,7 @@ export class FiberNode { this.memorizedProps = null this.updateQueue = null this.memorizedState = null + this.deletions = null this.alternate = null // effect @@ -67,7 +69,7 @@ export class FiberRootNode { } } -export const crateWorkInProgress = ( +export const createWorkInProgress = ( current: FiberNode, pendingProps: Props ): FiberNode => { @@ -85,6 +87,7 @@ export const crateWorkInProgress = ( // reset effect wip.flags = NoFlags wip.subtreeFlags = NoFlags + wip.deletions = null } wip.type = current.type diff --git a/packages/react-reconciler/src/workLoop.ts b/packages/react-reconciler/src/workLoop.ts index 922a4ab..c5f5a4e 100644 --- a/packages/react-reconciler/src/workLoop.ts +++ b/packages/react-reconciler/src/workLoop.ts @@ -3,7 +3,7 @@ * @Date: 2023-11-02 19:42:15 * @Description: */ -import { FiberNode, FiberRootNode, crateWorkInProgress } from './fiber' +import { FiberNode, FiberRootNode, createWorkInProgress } from './fiber' import { beginWork } from './beginWork' import { completeWork } from './completeWork' import { HostRoot } from './workTags' @@ -13,7 +13,7 @@ import { commitMutationEffects } from './commitWork' let workInProgress: FiberNode | null = null const prepareFreshStack = (root: FiberRootNode) => { - workInProgress = crateWorkInProgress(root.current, {}) + workInProgress = createWorkInProgress(root.current, {}) } export const scheduleUpdateOnFiber = (fiber: FiberNode) => { diff --git a/packages/react/index.ts b/packages/react/index.ts index ea0b1b5..40d7d5c 100644 --- a/packages/react/index.ts +++ b/packages/react/index.ts @@ -8,7 +8,7 @@ import { resolveDispatcher, currentDispatcher } from './src/currentDispatcher' -import { jsxDEV } from './src/jsx' +import { jsx, isValidElement as isValidElementFn } from './src/jsx' export const useState: Dispatcher['useState'] = (initialState) => { const dispatcher = resolveDispatcher() @@ -19,7 +19,8 @@ export const _Inner_data = { currentDispatcher } -export default { - version: '0.0.1', - createElement: jsxDEV -} +export const version = '0.0.0' + +// distinguish jsx and jsxDev by environment +export const createElement = jsx +export const isValidElement = isValidElementFn diff --git a/packages/react/src/jsx.ts b/packages/react/src/jsx.ts index c8c8be4..99e0c62 100644 --- a/packages/react/src/jsx.ts +++ b/packages/react/src/jsx.ts @@ -31,6 +31,14 @@ const ReactElement = ( return element } +export const isValidElement = (object: any) => { + return ( + typeof object === 'object' && + object !== null && + object.$$typeof === REACT_ELEMENT_TYPE + ) +} + export const jsx = ( type: ElementType, config: any, diff --git a/tsconfig.json b/tsconfig.json index 970c5fe..3db2210 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,6 @@ { "compileOnSave": true, + "include": ["./packages/**/*"], "compilerOptions": { /* Visit https://aka.ms/tsconfig to read more about this file */