1

之间的区别 -

this.setState({value: 'xyz', name: 'john', color: 'orange'}) 

对比

setValue('xyz'); 
setName('john');
setColor('orange');

挂钩是否同步工作/使用? 当它在第一个 setValue 或 setColor 状态之后开始实际渲染时?

我还想知道它在引擎盖下是如何工作的?

4

3 回答 3

2

setState 将先前的状态与新的状态合并,这意味着您不必每次想要更改状态的某些部分时都传递完整的状态对象。

React 将更新给定的属性并且不会触及其余的。

useState 的更新器用新的状态重写先前的状态,并且它不执行任何合并。

它只是替换而不是合并。

于 2020-05-03T13:50:17.813 回答
1

1. 在幕后,hook 像链表一样存储

https://medium.com/flatiron-labs/break-the-rules-of-react-hooks-9e892636641e

https://github.com/facebook/react/blob/9f395904c6033598ba8bf47f5497fd6e5077c16d/packages/react-reconciler/src/ReactFiberHooks.js

Hooks 以链表的形式存储在 Fiber 的 memoizedState 字段中。当前钩子列表是属于当前纤程的列表。work-in-progress hook 列表是一个新列表,将添加到 work-in-progress 纤程中。

2. Hook 一个一个执行,每次渲染都基于一个全新的 Hook 链表

因此,在多个 useState 挂钩的情况下,与仅在一个状态对象中一起更新或执行状态的类组件不同,每次更改状态都会触发重新渲染。Hook 是单独执行的,改变一个 Hook 不会立即导致组件渲染,这取决于 React Fiber 的核心算法。

根据上面的文章,每次渲染都会创建一个新的钩子链表,因此每次渲染都必须以相同的顺序调用钩子。

通过 React 的文档: https ://reactjs.org/docs/hooks-rules.html#explanation

...是 React 依赖于Hooks 的调用顺序。

如果第一次渲染和第二次渲染的顺序不同,则会导致错误。

关于你的例子,假设你有这样的代码:

 const [value, setValue] = useState('xyz');
 const [name, setName] = useState('John');
 const [color,setColor] = useState('Orange')

然后 react 会构建一个 hook 链表,头节点是 setValue hook,下一个节点是 setName,下一个节点是 setColor。并且在 setValue('abc')执行时,react 会从旧链表中拉出头节点,改变它的值,并引用它的下一个节点来创建一个新的链表。如果setName('Bill')接下来执行此行,则 react 将再次执行上述操作。

但是何时触发组件重新渲染?实际上,我不确定,但我们可以使用如下示例。

案例测试1

在函数handleClick内部,当setCount(count + 1)执行时,react不会立即渲染组件,当执行下一行时setValue(value + count)count仍然引用旧状态。点击 5 次,该组件共渲染 6 次。

案例测试2

在这种情况下,第二个设置状态在执行setValue(value + count)后 3 秒后触发setCount(count + 1)。结果是,当 setCount(count + 1)执行时,组件立即渲染,执行后3秒后setValue(value + count),组件再次渲染,但状态count仍然引用旧状态。点击 5 次,该组件共渲染 11 次。

所以,这取决于react fiber的核心算法。

你可以试试下面的代码:

案例测试1

const {useState} = React;

const App=()=> {
  console.log("render App")
  const [count, setCount] = useState(0);
  const [value, setValue] = useState('xyz');
  
  //console.log(value)
  if(count> 5)  //Before count >5 , the order of hooks is : 1.setCount, 2.setValue, 3.setColor , when count is above 5, then change the order of hooks as : 1.setCount, 2.setValue, 3.setName, 4.setColor
  {
    const [name, setName] = useState('John');
  }
  
  const [color,setColor] = useState('Orange')
 
  function handleClick(e) {
    setCount(count + 1)
    setValue(value + count)
    //setTimeout(()=>setValue(value + count),3000)
    
  }
 

  return (
    <div>
      <p>You clicked {count} times, when above 5 times, this function would crash, because order of hooks is changed</p>
      <p>And the 'value' is : {value}, and you might find value didn't plus the new state of count, but old state of count</p>
      <button onClick={handleClick}>
        Click me
      </button>
    </div>
  );
}



ReactDOM.render(
  <App/>,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>

案例测试2

const {useState} = React;

const App=()=> {
  console.log("render App")
  const [count, setCount] = useState(0);
  const [value, setValue] = useState('xyz');
  
  //console.log(value)
  if(count> 5)  //Before count >5 , the order of hooks is : 1.setCount, 2.setValue, 3.setColor , when count is above 5, then change the order of hooks as : 1.setCount, 2.setValue, 3.setName, 4.setColor
  {
    const [name, setName] = useState('John');
  }
  
  const [color,setColor] = useState('Orange')
 
  function handleClick(e) {
    setCount(count + 1)
    //setValue(value + count)
    setTimeout(()=>setValue(value + count),3000)        
  }
 

  return (
    <div>
      <p>You clicked {count} times, when above 5 times, this function would crash, because order of hooks is changed</p>
      <p>And the 'value' is : {value}, and you might find value didn't plus the new state of count, but old state of count</p>
      <button onClick={handleClick}>
        Click me
      </button>
    </div>
  );
}



ReactDOM.render(
  <App/>,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>

于 2020-05-03T15:03:49.203 回答
0

在您的示例中,whit hook,您的组件重新渲染了三次。但你可以写:

const [data, setData] = useState({value: '', name: '', color: ''});

并将它们放在一起:

setData({...data, value: 'xyz', name: 'john', color: 'red'} );

现在没有区别了。

通常,useState()Hook 的唯一参数是初始状态。与类不同,状态不必是对象。如果这就是我们所需要的,我们可以保留一个数字或一个字符串。与setState类组件中的方法不同,useState它不会自动合并更新对象。

于 2020-05-03T13:52:06.850 回答