JavaScript 反应钩子背后的机制是如何工作的?

How does JavaScript mechanism behind react hooks work?

我的问题与 Javascript 使反应挂钩成为可能的机制有关。

React 的最新开发允许我们创建钩子,即。对于 React 状态,在像这样的简单函数中:

function App () {
  const [someVar, setSomeVar] = useState('someVarDefaultValue');
  return (
    <div 
      onClick={() => setSomeVar('newValue')}>{someVar}
    </div>
  );
}

钩子 useState returns 一个带有访问器和修改器的数组,我们在 App 函数中通过数组分解来使用它们。

所以在幕后,钩子看起来像(只是一个伪代码):

function useState(defaultValue) {
  let value = defaultValue;

  function setValue(val) {
    value = val;
  }

  return [value, setValue];
}

当你在 JS 中尝试这种方法时,它不会起作用 - 如果你在某处使用 setValue,从数组分解的值将不会更新。即使您使用 value 作为对象,而不是原始 defaultValue.

我的问题是钩子机制在JS中是如何工作的?

根据我在 React sourcecode 中看到的内容,它使用 reducer 函数并使用 Flow 进行类型检查。为了理解全局,代码很难理解。

这个问题不是关于 how to write custom hooks in React

在这个问题中回答的 React 状态管理上下文中钩子如何在幕后工作也不是问题:

状态值必须存储在 useState 函数之外,在组件实例的某些内部表示中,以便它 returns 跨调用持久结果。此外,设置该值必须导致在调用它的组件上重新呈现:

     // useState must have a reference to the component it was called in:
     let context;

     function useState(defaultValue) {
       // Calling useState outside of a component won't work as it needs the context:
       if(!context) throw new Error("Can only be called inside render");
       // Only initialize the context if it wasn't rendered yet (otherwise it would re set the value on a rerender)
       if(!context.value)
        context.value = defaultValue;
       // Memoize the context to be accessed in setValue
       let memoizedContext = context;
       function setValue(val) {
          memoizedContext.value = val;
          // Rerender, so that calling useState will return the new value
          internalRender(memoizedContext);
       }

      return [context.value, setValue];
     }

    // A very simplified React mounting logic:
    function internalRender(component) {
       context = component;
       component.render();
       context = null;
    }

     

     // A very simplified component
     var component = {
      render() {
        const [value, update] = useState("it");
        console.log(value);
        setTimeout(update, 1000, "works!");
      }
    };

    internalRender(component);

然后当 setValue 被调用时,组件重新渲染,useState 将再次被调用,并返回新值。

上面的例子非常简单,这里有一些 React 不同的地方:

(1) 状态不存储在“上下文属性”中,而是存储在链表中。每当调用 useState 时,链表都会前进到下一个节点。这就是为什么你不应该在分支/循环中使用钩子。

(2) setState 函数被缓存并且每次都返回相同的引用。

(3) 重新渲染不会同步发生。