react相关

Posted on:December 7, 2023 at 10:41 AM
预计阅读时长:13 min read 字数:2567

目录

react use and read source code

react 使用以及源码阅读

 npm link react react-dom

实际上是将你正在开发的 React 和 React DOM 的本地版本与你的项目进行了“链接”,允许你实时进行更改测试,而无需反复将你的包发布到 npm.

processing

查看执行过程

react_process

todo:使用 react 大部分 feature 的一个小组件

react flow

React 源码中的这些类型定义是 Flow 类型系统的一部分.Flow 是一种 JavaScript 的静态类型检查工具,它允许在代码中添加类型注解,帮助开发者检测潜在的类型错误

export type StatelessFunctionalComponent<P> =
  React$StatelessFunctionalComponent<P>;
export type ComponentType<-P> = React$ComponentType<P>;
export type AbstractComponent<
  -Config,
  +Instance = mixed
> = React$AbstractComponent<Config, Instance>;
export type ElementType = React$ElementType;
export type Element<+C> = React$Element<C>;
export type Key = React$Key;
export type Ref<C> = React$Ref<C>;
export type Node = React$Node;
export type Context<T> = React$Context<T>;
export type Portal = React$Portal;
export type ElementProps<C> = React$ElementProps<C>;
export type ElementConfig<C> = React$ElementConfig<C>;
export type ElementRef<C> = React$ElementRef<C>;
export type Config<Props, DefaultProps> = React$Config<Props, DefaultProps>;
export type ChildrenArray<+T> = $ReadOnlyArray<ChildrenArray<T>> | T;

cd react

yarn cache clean -f

synchronous rendering process in React Fiber

  • 异步可中断

ReactFiberWorkLoop

/** @noinline */
function workLoopConcurrent() {
  // 执行工作,直到调度器要求暂停
  while (workInProgress !== null && !shouldYield()) {
    // $FlowFixMe[incompatible-call] 在升级 Flow 时发现的问题
    performUnitOfWork(workInProgress);
  }
}

Scheduler

function unstable_scheduleCallback(
  priorityLevel: PriorityLevel,
  callback: Callback,
  options?: { delay: number }
): Task {
  // 获取当前时间
  var currentTime = getCurrentTime();

  // 计算任务的开始时间
  var startTime;
  if (typeof options === "object" && options !== null) {
    var delay = options.delay;
    if (typeof delay === "number" && delay > 0) {
      startTime = currentTime + delay;
    } else {
      startTime = currentTime;
    }
  } else {
    startTime = currentTime;
  }

  // 根据优先级确定任务的超时时间
  var timeout;
  switch (priorityLevel) {
    case ImmediatePriority:
      // 立即超时
      timeout = -1;
      break;
    case UserBlockingPriority:
      // 最终会超时
      timeout = userBlockingPriorityTimeout;
      break;
    case IdlePriority:
      // 永不超时
      timeout = maxSigned31BitInt;
      break;
    case LowPriority:
      // 最终会超时
      timeout = lowPriorityTimeout;
      break;
    case NormalPriority:
    default:
      // 最终会超时
      timeout = normalPriorityTimeout;
      break;
  }

  // 计算任务的过期时间
  var expirationTime = startTime + timeout;

  // 创建一个新的任务对象
  var newTask: Task = {
    id: taskIdCounter++,
    callback,
    priorityLevel,
    startTime,
    expirationTime,
    sortIndex: -1,
  };

  // 如果开启了性能分析,进行相应的设置
  if (enableProfiling) {
    newTask.isQueued = false;
  }

  // 根据任务的开始时间或过期时间,将任务加入到相应的队列中
  if (startTime > currentTime) {
    // 这是一个延迟任务.
    newTask.sortIndex = startTime;
    push(timerQueue, newTask);
    if (peek(taskQueue) === null && newTask === peek(timerQueue)) {
      // 所有任务都是延迟任务,且这是最早延迟的任务.
      if (isHostTimeoutScheduled) {
        // 取消现有的超时.
        cancelHostTimeout();
      } else {
        isHostTimeoutScheduled = true;
      }
      // 安排一个超时.
      requestHostTimeout(handleTimeout, startTime - currentTime);
    }
  } else {
    // 这是一个即时任务或者延迟任务已经过期.
    newTask.sortIndex = expirationTime;
    push(taskQueue, newTask);
    if (enableProfiling) {
      markTaskStart(newTask, currentTime);
      newTask.isQueued = true;
    }
    // 如果没有已经安排的宿主回调,并且当前没有执行工作,则安排一个宿主回调.
    if (!isHostCallbackScheduled && !isPerformingWork) {
      isHostCallbackScheduled = true;
      requestHostCallback();
    }
  }

  return newTask;
}

lane

在 React 中,Lane 模型是一种用于调度和分配渲染优先级的机制.它是 React Fiber 架构的一部分,旨在提高渲染的效率和优先级管理.Lane 模型引入了一种将更新划分为不同优先级的概念,以便更好地响应用户操作并确保平滑的用户体验.

1.优先级(Priority): Lane 模型引入了多个不同的渲染优先级.不同的任务可以被分配到不同的优先级,例如 Immediate、UserBlocking、Normal、Low、Idle 等.这使得 React 能够在不同场景下灵活地分配和调整任务的优先级,以更好地响应用户交互.

2.Lanes: Lane 是用于标识任务所属优先级的概念.每个 Lane 表示一个特定的渲染优先级.React 使用 Lanes 来追踪任务的优先级,并根据不同的优先级调度任务的执行.任务会被分配到相应的 Lane 上,从而确定其优先级.

3.合并(Merging): Lanes 支持合并操作.当多个更新具有相同的优先级时,它们可以被合并到同一个 Lane 中,以减少不必要的渲染操作.这有助于提高性能,避免重复的工作.

4.中断和恢复: Lanes 允许中断和恢复渲染.在某些情况下,React 可能需要中断正在进行的工作以执行更紧急的任务.Lanes 模型提供了中断和恢复工作的机制,确保任务的有序执行.

5.划分策略: 不同的任务会根据其性质和紧急程度被分配到不同的 Lane.例如,用户交互相关的任务可能会被分配到 UserBlocking 优先级的 Lane,以确保快速响应.

6.渲染精细度: Lane 模型支持渲染的细粒度控制.通过划分为不同的 Lane,React 可以更精确地控制何时、何地以及以何种方式执行渲染工作.

Lane 模型的引入使得 React 能够更好地适应各种场景下的渲染需求,确保高优先级任务能够尽快得到处理,同时有效地管理低优先级任务的执行.这有助于提高 React 应用的性能和用户体验.

react components use class or function

  • class
import React, { Component } from "react";

interface LifecycleExampleState {
  counter: number;
}

class LifecycleExample extends Component<{}, LifecycleExampleState> {
  constructor(props: {}) {
    super(props);
    this.state = {
      counter: 0,
    };
    console.log("Constructor");
  }

  static getDerivedStateFromProps(
    nextProps: {},
    nextState: LifecycleExampleState
  ) {
    console.log("getDerivedStateFromProps", nextProps, nextState);
    return null;
  }

  componentDidMount() {
    console.log("componentDidMount");
  }

  shouldComponentUpdate(nextProps: {}, nextState: LifecycleExampleState) {
    console.log("shouldComponentUpdate", nextProps, nextState);
    return true;
  }

  getSnapshotBeforeUpdate(prevProps: {}, prevState: LifecycleExampleState) {
    console.log("getSnapshotBeforeUpdate", prevProps, prevState);
    return null;
  }

  componentDidUpdate(
    prevProps: {},
    prevState: LifecycleExampleState,
    snapshot: null
  ) {
    console.log("componentDidUpdate", prevProps, prevState, snapshot);
  }

  componentWillUnmount() {
    console.log("componentWillUnmount");
  }

  static getDerivedStateFromError(error: Error) {
    console.log("getDerivedStateFromError", error);
    return null;
  }

  componentDidCatch(error: Error, info: React.ErrorInfo) {
    console.log("componentDidCatch", error, info);
  }

  handleIncrement = () => {
    this.setState(prevState => ({
      counter: prevState.counter + 1,
    }));
  };

  render() {
    console.log("Render");
    return (
      <div>
        <h1>Counter: {this.state.counter}</h1>
        <button onClick={this.handleIncrement}>Increment</button>
      </div>
    );
  }
}

export default LifecycleExample;
  • function
import React, { useState, useEffect, useReducer } from "react";

interface LifecycleExampleProps {
  initialCounter: number;
}

interface LifecycleExampleState {
  counter: number;
}

type Action = { type: "increment" } | { type: "decrement" };

const counterReducer = (
  state: LifecycleExampleState,
  action: Action
): LifecycleExampleState => {
  switch (action.type) {
    case "increment":
      return { ...state, counter: state.counter + 1 };
    case "decrement":
      return { ...state, counter: state.counter - 1 };
    default:
      return state;
  }
};

const LifecycleExample: React.FC<LifecycleExampleProps> = ({
  initialCounter,
}) => {
  const [state, dispatch] = useReducer(counterReducer, {
    counter: initialCounter,
  });

  useEffect(() => {
    console.log("Component Did Mount");

    return () => {
      console.log("Component Will Unmount");
    };
  }, []); // Empty dependency array means it runs once on mount and once on unmount

  useEffect(() => {
    console.log("Counter Value Changed:", state.counter);
  }, [state.counter]); // Runs whenever counter value changes

  const handleIncrement = () => {
    dispatch({ type: "increment" });
  };

  const handleDecrement = () => {
    dispatch({ type: "decrement" });
  };

  console.log("Render");

  return (
    <div>
      <h1>Counter: {state.counter}</h1>
      <button onClick={handleIncrement}>Increment</button>
      <button onClick={handleDecrement}>Decrement</button>
    </div>
  );
};

export default LifecycleExample;

react practice

re-render
// This is a React Quiz from BFE.dev

import React, { useState, useEffect } from "react";

function A(props: { state: number }) {
  console.log("A");
  return <B />;
}

function B() {
  console.log("B");
  return <C />;
}

function C() {
  console.log("C");
  return null;
}

function D() {
  console.log("D");
  return null;
}

export default function App() {
  const [state, setState] = useState(0);
  useEffect(() => {
    setState(state => state + 1);
  }, []);
  console.log("App");
  return (
    <div>
      <A state={state} />
      <D />
    </div>
  );
}

result

"App";
"A";
"B";
"C";
"D";
"App";
"A";
"B";
"C";
"D";
re-render context

useContext

// This is a React Quiz from BFE.dev

import React, {
  useState,
  memo,
  createContext,
  useEffect,
  useContext,
} from "react";
import ReactDOM from "react-dom";

const MyContext = createContext(0);

function B() {
  const count = useContext(MyContext);
  console.log("B");
  return null;
}

const A = memo(() => {
  console.log("A");
  return <B />;
});

function C() {
  console.log("C");
  return null;
}
function App() {
  const [state, setState] = useState(0);
  useEffect(() => {
    setState(state => state + 1);
  }, []);
  console.log("App");
  return (
    <MyContext.Provider value={state}>
      <A />
      <C />
    </MyContext.Provider>
  );
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);

result

"App";
"A";
"B";
"C";
"App";
"B";
"C";
suspense
// This is a React Quiz from BFE.dev

import React, { Suspense } from "react";
import ReactDOM from "react-dom";

const resource = (() => {
  let data = null;
  let status = "pending";
  let fetcher = null;
  return {
    get() {
      if (status === "ready") {
        return data;
      }
      if (status === "pending") {
        fetcher = new Promise((resolve, reject) => {
          setTimeout(() => {
            data = 1;
            status = "ready";
            resolve();
          }, 100);
        });
        status = "fetching";
      }

      throw fetcher;
    },
  };
})();

function A() {
  console.log("A1");
  const data = resource.get();
  console.log("A2");
  return <p>{data}</p>;
}

function Fallback() {
  console.log("fallback");
  return null;
}

function App() {
  console.log("App");
  return (
    <div>
      <Suspense fallback={<Fallback />}>
        <A />
      </Suspense>
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
"App";
"A1";
"fallback";
"A1";
"A2";
suspenseB
// This is a React Quiz from BFE.dev

import { Suspense } from "react";

const resource = (() => {
  let data = null;
  let status = "pending";
  let fetcher = null;
  return {
    get() {
      if (status === "ready") {
        return data;
      }
      if (status === "pending") {
        fetcher = new Promise((resolve, reject) => {
          setTimeout(() => {
            data = "success";
            status = "ready";
            resolve("success");
          }, 100);
        });
        status = "fetching";
      }

      throw fetcher;
    },
  };
})();

function A() {
  console.log("SuspenseB A1");
  const data = resource.get();
  console.log("SuspenseB A2");
  return <p>{data}</p>;
}

function B() {
  console.log("SuspenseB B");
  return null;
}

function Fallback() {
  console.log("SuspenseB fallback");
  return null;
}

export default () => {
  console.log("SuspenseB App");
  return (
    <div>
      <Suspense fallback={<Fallback />}>
        <A />
        <B />
      </Suspense>
    </div>
  );
};

/* 
App
A1
B
fallback
A1
A2
B
*/
reRender context
import { useState, createContext, useEffect, useContext } from "react";

const MyContext = createContext(0);

function B({ children }) {
  const count = useContext(MyContext);
  console.log("B");
  return children;
}

const A = ({ children }) => {
  const [state, setState] = useState(0);
  console.log("A");
  useEffect(() => {
    setState(state => state + 1);
  }, []);
  return <MyContext.Provider value={state}>{children}</MyContext.Provider>;
};

function C() {
  console.log("C");
  return null;
}

function D() {
  console.log("D");
  return null;
}
export default () => {
  console.log("App");
  return (
    <A>
      <B>
        <C />
      </B>
      <D />
    </A>
  );
};
Click to expand
  • useLayoutEffect is a version of useEffect that fires before the browser repaints the screen.

react source code

Build your own React

Step I: The createElement Function
Step II: The render Function
Step III: Concurrent Mode
Step IV: Fibers
Step V: Render and Commit Phases
Step VI: Reconciliation
Step VII: Function Components
Step VIII: Hooks

step0 basic
const element = {
  type: "h1",
  props: {
    title: "foo",
    children: "Hello",
  },
}

const container = document.getElementById("root")

const node = document.createElement(element.type)
node["title"] = element.props.title

const text = document.createTextNode("")
text["nodeValue"] = element.props.children

node.appendChild(text)
container.appendChild(node)
Step I: createElement

Didact.createElement

function createElement(type, props, ...children) {
  return {
    type,
    props: {
      ...props,
      children: children.map(child =>
        typeof child === "object" ? child : createTextElement(child)
      ),
    },
  };
}
Step II: render
function createElement(type, props, ...children) {
  return {
    type,
    props: {
      ...props,
      children: children.map(child =>
        typeof child === "object"
          ? child
          : createTextElement(child)
      ),
    },
  }
}

function createTextElement(text) {
  return {
    type: "TEXT_ELEMENT",
    props: {
      nodeValue: text,
      children: [],
    },
  }
}

function render(element, container) {
  const dom =
    element.type == "TEXT_ELEMENT"
      ? document.createTextNode("")
      : document.createElement(element.type)

  const isProperty = key => key !== "children"
  Object.keys(element.props)
    .filter(isProperty)
    .forEach(name => {
      dom[name] = element.props[name]
    })

  element.props.children.forEach(child =>
    render(child, dom)
  )

  container.appendChild(dom)
}

const Didact = {
  createElement,
  render,
}

/** @jsx Didact.createElement */
const element = (
  <div id="foo">
    <a>bar</a>
    <b />
  </div>
)
const container = document.getElementById("root")
Didact.render(element, container)
Step III: Concurrent Mode

一旦开始渲染,我们就不会停止,直到渲染出完整的元素树.如果元素树很大,可能会阻塞主线程太长时间.如果浏览器需要执行高优先级的操作,例如处理用户输入或保持动画流畅,则必须等到渲染完成.

let nextUnitOfWork = null

function workLoop(deadline) {
  let shouldYield = false
  while (nextUnitOfWork && !shouldYield) {
    nextUnitOfWork = performUnitOfWork(
      nextUnitOfWork
    )
    shouldYield = deadline.timeRemaining() < 1
  }
  requestIdleCallback(workLoop)
}

requestIdleCallback(workLoop)

function performUnitOfWork(nextUnitOfWork) {
  // TODO
}

react 19要来力

  1. Complier
  2. useActionState
  3. useOptimistic
  4. use
  5. cache
  6. SEO
  7. Wep Component
  8. ref prop

link