Skip to content

Commit

Permalink
七: 实现 hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
zh-lx committed Nov 10, 2021
1 parent a0e9d46 commit b796bca
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 5 deletions.
8 changes: 7 additions & 1 deletion src/index.js
@@ -1,4 +1,4 @@
import { Component } from './mini-react/react';
import { Component, useState } from './mini-react/react';
import ReactDOM from './mini-react/react-dom';
import './index.css';

Expand Down Expand Up @@ -27,10 +27,16 @@ class ClassComponent extends Component {
}

function FunctionComponent(props) {
const [count, setCount] = useState(0);
const addCount = () => {
setCount(count + 1);
};
return (
<div className="function-component">
<div>this is a function Component</div>
<div>prop value is: {props.value}</div>
<div>count is: {count}</div>
<input type="button" value="add count" onClick={addCount} />
</div>
);
}
Expand Down
26 changes: 23 additions & 3 deletions src/mini-react/fiber.js
Expand Up @@ -6,6 +6,8 @@ let nextUnitOfWork = null;
let workInProgressRoot = null; // 当前工作的 fiber 树
let currentRoot = null; // 上一次渲染的 fiber 树
let deletions = []; // 要执行删除 dom 的 fiber
let currentFunctionFiber = null; // 当前正在执行的函数组件对应 fiber
let hookIndex = 0; // 当前正在执行的函数组件 hook 的下标

// 将某个 fiber 加入 deletions 数组
export function deleteFiber(fiber) {
Expand All @@ -27,6 +29,16 @@ export function commitRender() {
nextUnitOfWork = workInProgressRoot;
}

// 获取当前的执行的函数组件对应的 fiber
export function getCurrentFunctionFiber() {
return currentFunctionFiber;
}

// 获取当前 hook 下标
export function getHookIndex() {
return hookIndex++;
}

// 创建 rootFiber 作为首个 nextUnitOfWork
export function createRoot(element, container) {
workInProgressRoot = {
Expand Down Expand Up @@ -57,9 +69,7 @@ function performUnitOfWork(workInProgress) {
updateClassComponent(workInProgress);
} else {
// 函数组件
const { props, type: Fn } = workInProgress.element;
const jsx = Fn(props);
children = [jsx];
updateFunctionComponent(workInProgress);
}
}

Expand Down Expand Up @@ -114,6 +124,16 @@ function updateClassComponent(fiber) {
reconcileChildren(fiber, [jsx]);
}

// 函数组件的更新
function updateFunctionComponent(fiber) {
currentFunctionFiber = fiber;
currentFunctionFiber.hooks = [];
hookIndex = 0;
const { props, type: Fn } = fiber.element;
const jsx = Fn(props);
reconcileChildren(fiber, [jsx]);
}

// 处理循环和中断逻辑
function workLoop(deadline) {
let shouldYield = false;
Expand Down
33 changes: 32 additions & 1 deletion src/mini-react/react.js
@@ -1,4 +1,4 @@
import { commitRender } from './fiber';
import { commitRender, getCurrentFunctionFiber, getHookIndex } from './fiber';
export class Component {
constructor(props) {
this.props = props;
Expand Down Expand Up @@ -26,3 +26,34 @@ Component.prototype.setState = function (param) {
Component.prototype._UpdateProps = function (props) {
this.props = props;
};

export function useState(initial) {
const currentFunctionFiber = getCurrentFunctionFiber();
const hookIndex = getHookIndex();
// 取当前执行的函数组件之前的 hook
const oldHook = currentFunctionFiber?.alternate?.hooks?.[hookIndex];

// oldHook存在,取之前的值,否则取现在的值
const hook = {
state: oldHook ? oldHook.state : initial,
queue: [], // 一次函数执行过程中可能调用多次 setState,将其放进队列一并执行
};

const actions = oldHook ? oldHook.queue : [];
actions.forEach((action) => {
hook.state = action(hook.state);
});

const setState = (action) => {
if (typeof action === 'function') {
hook.queue.push(action);
} else {
hook.queue.push(() => {
return action;
});
}
commitRender();
};
currentFunctionFiber.hooks.push(hook);
return [hook.state, setState];
}

0 comments on commit b796bca

Please sign in to comment.