美文网首页
React 运行机制解析

React 运行机制解析

作者: 弹指一挥间_e5a3 | 来源:发表于2022-02-11 15:20 被阅读0次
for (var i=0;i<6;i++){
    setTimeout(()=>{
         console.log(i) 
    })
}
for (var i=0;i<6;i++){
    !function(i){
    setTimeout(()=>{
         console.log(i) 
    })
    }(i)
}

React 每一次渲染都有它自己的 Props and State

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

count 会“监听”状态的变化并自动更新吗?这么想可能是学习React的时候有用的第一直觉。

上面例子中,count仅是一个数字而已。它不是神奇的“data binding”, “watcher”, “proxy”,或者其他任何东西。它就是一个普通的数字像下面这个一样:

//  第一次
function Counter() {
  const count = 0; 
  // ...
  <p>You clicked {count} times</p>
  // ...
}

// 第二次
function Counter() {
  const count = 1; 
  // ...
  <p>You clicked {count} times</p>
  // ...
}

// 第三次
function Counter() {
  const count = 2; 
  // ...
  <p>You clicked {count} times</p>
  // ...
}

当我们更新状态的时候,React 会重新渲染组件。每一次渲染都能拿到独立的 count 状态,这个状态值是函数中的一个常量。

它仅仅只是在渲染输出中插入了 count 这个数字。这个数字由 Reac t提供。当 setCount 的时候,React 会带着一个不同的 count 值再次调用组件。然后,React 会更新 DOM 以保持和渲染输出一致。

这里关键的点在于任意一次渲染中的 count 常量都不会随着时间改变。渲染输出会变是因为我们的组件被一次次调用,而每一次调用引起的渲染中,它包含的count 值独立于其他渲染。

每一次渲染都有它自己的事件处理函数

function Counter() {
  const [count, setCount] = useState(0);

  function handleAlertClick() {
    setTimeout(() => {
      alert('You clicked on: ' + count);
    }, 3000);
  }

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
      <button onClick={handleAlertClick}>
        Show alert
      </button>
    </div>
  );
}

你猜 alert 会弹出什么呢?会是 5 吗?— 这个值是 alert 的时候 counter 的实时状态。或者会是 3 吗?— 这个值是我点击时候的状态。

但它究竟是如何工作的呢?

我们发现 count 在每一次函数调用中都是一个常量值。值得强调的是 — 我们的组件函数每次渲染都会被调用,但是每一次调用中 count 值都是常量,并且它被赋予了当前渲染中的状态值。

这并不是 React 特有的,普通的函数也有类似的行为:

function sayHi(person) {
  const name = person.name;
  setTimeout(() => {
    alert('Hello, ' + name);
  }, 3000);
}

let someone = {name: 'Dan'};
sayHi(someone);

someone = {name: 'Yuzhi'};
sayHi(someone);

someone = {name: 'Dominic'};
sayHi(someone);

在这个例子会被赋值很多次(就像在 React 中,当前的组件状态会改变一样)。然后,在sayHi函数中,局部常量name会和某次调用中的person关联。因为这个常量是局部的,所以每一次调用都是相互独立的。结果就是,当定时器回调触发的时候,每一个alert都会弹出它拥有的name

这就解释了我们的事件处理函数如何捕获了点击时候的count值。如果我们应用相同的替换原理,每一次渲染“看到”的是它自己的count

// 第一次
function Counter() {
  const count = 0; 
  // ...
  function handleAlertClick() {
    setTimeout(() => {
      alert('You clicked on: ' + count);
    }, 3000);
  }
  // ...
}

// 第二次
function Counter() {
  const count = 1; 
  // ...
  function handleAlertClick() {
    setTimeout(() => {
      alert('You clicked on: ' + count);
    }, 3000);
  }
  // ...
}

// 第三次
function Counter() {
  const count = 2; 
  // ...
  function handleAlertClick() {
    setTimeout(() => {
      alert('You clicked on: ' + count);
    }, 3000);
  }
  // ...
}

所以实际上,每一次渲染都有一个新版本handleAlertClick 。每一个版本的 handleAlertClick 记住 了它自己的 count

每一次渲染都有它自己的…所有

我们现在知道effects会在每次渲染后运行,并且概念上它是组件输出的一部分,可以“看到”属于某次特定渲染的props和state。

function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    setTimeout(() => {
      console.log(`You clicked ${count} times`);
    }, 3000);
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

小结一下

组件内的每一个函数(包括事件处理函数,effects,定时器或者API调用等等)会捕获定义它们的那次渲染中的props和state。
到目前为止,我们可以明确地知道下面重要的事实:每一个组件内的函数(包括事件处理函数,effects,定时器或者API调用等等)会捕获某次渲染中定义的props和state。

在组件内什么时候去读取props或者state是无关紧要的。因为它们不会改变。在单次渲染的范围内,props和state始终保持不变。(解构赋值的props使得这一点更明显。)

当然,有时候你可能在effect的回调函数里读取最新的值而不是捕获的值。最简单的实现方法是使用refs。

function Example() {
  const [count, setCount] = useState(0);
  const latestCount = useRef(count);

  useEffect(() => {
    // Set the mutable latest value
    latestCount.current = count;
    setTimeout(() => {
      // Read the mutable latest value
      console.log(`You clicked ${latestCount.current} times`);
    }, 3000);
  });
  // ...

在React中去直接修改值看上去有点怪异。然而,在 class 组件中 React 正是这样去修改 this.state 的。不像捕获的 propsstate ,你没法保证在任意一个回调函数中读取的 latestCount.current 是不变的。根据定义,你可以随时修改它。这就是为什么它不是默认行为,而是需要你主动选择这样做。

相关文章

网友评论

      本文标题:React 运行机制解析

      本文链接:https://www.haomeiwen.com/subject/bbvjtrtx.html