1. За сценой хуки хранятся как список ссылок
https://medium.com/flatiron-labs/breaking-the-rules-of-react-hooks-9e892636641e
https://github.com/facebook/react/blob/9f395904c6033598ba8bf47f5497fd6e5077c16d/packages/react-reconciler/src/ReactFiberHooks.js
Хуки хранятся в виде связанного списка в поле memoizedState волокна. Текущий список подключений - это список, который принадлежит текущему волокну. Список подключений в процессе выполнения - это новый список, который будет добавлен в волокно в процессе выполнения.
2. Хук выполняется один за другим, и каждый рендеринг основан на новом списке связанных хуков
Таким образом, в случае множественных хуков useState отличается от компонента класса, состояние которого обновляется или выполняется вместе только в одном объекте состояния каждый раз изменяющееся состояние вызывает повторное рендеринг. Хук выполняется отдельно, и замена одного хука не вызовет рендеринга компонента немедленно, это зависит от алгоритма ядра реагирующего волокна.
И, согласно статье выше, каждый рендер создает новый список, связанный с хуком, поэтому hooks должны вызываться в одном и том же порядке для каждого рендера.
Согласно документу React: https://reactjs.org/docs/hooks-rules.html#explanation
... это то, что React полагается на порядок , в котором вызываются хуки.
Если порядок первого и второго рендеров различен, это может привести к ошибке.
А что касается вашего примера, предположим, что у вас есть код, подобный следующему:
const [value, setValue] = useState('xyz');
const [name, setName] = useState('John');
const [color,setColor] = useState('Orange')
Затем при реакции будет создан список ссылок для перехвата, а головной узел - это перехватчик setValue, следующим узлом будет setName, и следующий узел будет setColor. И когда setValue('abc')
выполнено, реагирует на извлечение головного узла из старого связанного списка, изменение его значения и обращение к следующему его узлу для создания нового связного списка. И если эта строка setName('Bill')
будет выполнена следующей, тогда ответ будет делать то же самое, что и выше.
Но когда перезапуск компонента будет запущен? На самом деле, я не уверен, но мы могли бы использовать пример, как показано ниже.
case test1
Внутри функции handleClick, когда выполняется setCount(count + 1)
, реагировать не будет немедленно выполнять рендеринг компонента, а при выполнении следующей строки setValue(value + count)
count
все еще относится к старому состоянию. При щелчке 5 раз этот компонент отображается полностью 6 раз.
case test2
В этом случае второе установленное состояние setValue(value + count)
запускается через 3 секунды после setCount(count + 1)
выполнен. В результате, когда setCount(count + 1)
выполняется, компонент рендерит немедленно, через 3 секунды после выполнения setValue(value + count)
, компонент рендерит снова, но состояние count
все еще ссылается на старое состояние. При щелчке 5 раз этот компонент рендерится полностью 11 раз.
Итак, это зависит от алгоритма ядра реагирующего волокна.
Вы можете попробовать следующий код:
case test1
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>
case test2
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>