【问题标题】:How React implements its hooks (at a high level) [duplicate]React 如何实现它的钩子(在高层次上)[重复]
【发布时间】:2019-12-17 08:54:31
【问题描述】:

假设你有这样的事情:

const MyComponent = (props) => {
  const refA = React.useRef()
  const refB = React.useRef()
  const refC = React.useRef()
  const [x, setX] = React.useState(true)
  const [y, setY] = React.useState([])

  return <div>...</div>
}

在伪代码中,当它被多次调用时,它实际上在做什么?他们基本上是这样做的吗?

  • 知道MyComponent即将从函数转换为虚拟树。
  • 设置一个全局变量,该变量将在单个程序框架内调用每个 useX 挂钩时进行跟踪。
  • 为这个组件实例存储每个钩子调用的输出。
  • 下次为此组件 ID 调用钩子时,它会从该组件内的最后一次 useX 调用中获取最后返回结果的映射

我问是因为 (a) 它似乎只能通过某种全局变量技巧来完成,并且 (b) 对应的 code 相当复杂且难以解析。

想知道是否可以简要介绍一下 React 如何实现这些神奇的钩子的一些伪代码。

看起来有点像这样,虽然更高级:

let dispatcher = {}

function MyComponent() {

}

function begin() {
  dispatcher.component = {
    refs: [],
    states: []
  }
}

function useRef() {
  let ref = {}
  dispatcher.component.refs.push(ref)
  return ref
}

function useState(val) {
  let state = val
  dispatcher.component.states.push(val)
  return state
}

function end() {
  dispatcher.component = null
}

我只是不明白它是如何用这种魔法记忆的。

这与知道钩子如何知道它们用于哪个组件的问题不同。这只是我的问题的一个方面,我一般问的是钩子是如何工作的。

【问题讨论】:

  • 在内部我认为它使用 reducer 来跟踪组件的状态,并返回一个方法,您可以调用该方法来更新状态。它在理论上是全局的,但仍然在 React 的框架内。我建议你看一下 React 的 Smoosh 模式视频,它突出了关于内部工作原理的大量细节,它非常先进,但一旦你理解它就会非常令人愉悦:youtube.com/watch?v=aS41Y_eyNrU
  • 您是在问 (a) hooks 是如何实现的,还是 (b) 在每次调用时会发生什么反应?
  • 两者,钩子实际上是如何工作的,这意味着它被调用时会发生什么。渲染部分我不需要知道,主要是钩子是如何工作的。
  • 文档中有一个非常高级的描述:reactjs.org/docs/…

标签: reactjs internals


【解决方案1】:

关于如何实现钩子的问题非常广泛,可以解释每个钩子,让我们以useRefuseState为例。

虽然useRef(initialValue) 只是一个带有current 键的对象,但可能的实现:

const myRef = useRef('myReference');
const useRef = (initialValue) => ({ current: initialValue });

Hooks are just arrays,此数组引用状态 - 它不与其他组件共享,但它维护在特定组件的后续渲染可访问的范围内。

在每次调用setter函数时,我们可以使用游标来定义需要使用的状态。

useState的可能实现:

const state = [];
const setters = [];
let firstRun = true;
let cursor = 0;

function createSetter(cursor) {
  return function setterWithCursor(newVal) {
    state[cursor] = newVal;
  };
}

// This is the pseudocode for the useState helper
export function useState(initVal) {
  if (firstRun) {
    state.push(initVal);
    setters.push(createSetter(cursor));
    firstRun = false;
  }

  const setter = setters[cursor];
  const value = state[cursor];

  cursor++;
  return [value, setter];
}

// Our component code that uses hooks
function RenderFunctionComponent() {
  const [firstName, setFirstName] = useState("Rudi"); // cursor: 0
  const [lastName, setLastName] = useState("Yardley"); // cursor: 1

  return (
    <div>
      <Button onClick={() => setFirstName("Richard")}>Richard</Button>
      <Button onClick={() => setFirstName("Fred")}>Fred</Button>
    </div>
  );
}

// This is sort of simulating Reacts rendering cycle
function MyComponent() {
  cursor = 0; // resetting the cursor
  return <RenderFunctionComponent />; // render
}

console.log(state); // Pre-render: []
MyComponent();
console.log(state); // First-render: ['Rudi', 'Yardley']
MyComponent();
console.log(state); // Subsequent-render: ['Rudi', 'Yardley']

// click the 'Fred' button

console.log(state); // After-click: ['Fred', 'Yardley']

【讨论】:

  • 您的useState 仅适用于单个组件。这将如何适用于多个组件?
  • “你的 useState 只适用于单个组件”,为什么会这样?
  • if (firstRun) { memoization 是如何工作的等等
  • 每个组件都有自己的状态,不然我不明白你说的“多个组件”是什么意思
  • 请阅读答案中的文章,应用内所有组件无需保存状态,react调用特定组件的render函数。
猜你喜欢
  • 2019-07-28
  • 1970-01-01
  • 2019-04-23
  • 2019-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多