Чтобы ответить «почему реакция запускает всю функцию», ответ состоит в том, что javascript функции работают таким образом: вам всегда нужно запускать все это, они не останавливаются в середине *. Я понимаю, что вы думаете здесь, если вы привыкли к компонентам класса: разве у меня нет секции конструктора и секции рендеринга? и ответ: не совсем, если вы используете функциональные компоненты. У вас есть только рендер. Но крючки - это волшебные c, и они позволяют вам притворяться, что они состоят из двух частей.
Крючки знают, когда они вызываются, и, если вы всегда вызываете их в одном и том же порядке, они могут отслеживать состояние вне функция рендеринга. таким образом, работа выглядит примерно так:
- React обнаруживает компонент функции и создает или повторно использует существующий контекст рендеринга для этого компонента. Здесь хранится информация о перехватчиках.
- React вызывает ваш функциональный компонент, и он начинает работать.
- Вы вызываете перехватчики внутри своего функционального компонента. Они проверяют текущий контекст рендеринга и сохраняют / получают соответствующую информацию из этого контекста. В некотором смысле контекст рендеринга является «глобальной» переменной.
- Вы делаете все, что хотите внутри функции, и в конечном итоге возвращаете дерево компонентов (JSX) или ноль.
- реагирует затем ( в конечном итоге) обновляет DOM в соответствии с тем, что вы вернули, и сохраняет изменения в контексте рендеринга, чтобы при следующем вызове рендера он мог повторно использовать контекст.
Маги c в том, что контекст рендеринга может делать причудливые вещи с хуками, например, запускать их только один раз, всегда возвращать одно и то же значение из хука или любого другого количества вещей. Но в некотором смысле компонент «класс» становится контекстом рендеринга внутреннего реагирования, к которому хуки знают, как обращаться.
Вот пример useState
хука, реализованного в компоненте класса: (Вам никогда не понадобится это делать, но это пример того, как работают хуки).
class FakeHook extends React.Component {
constructor(...args) {
super(...args)
this.state = {}
this.useStateCalls = 0
}
useState(defaultValue){
const currentRenderContext = this.state
let value = defaultValue
const currentStateKey = `useState${this.useStateCalls}`
if (currentStateKey in currentRenderContext) value = currentRenderContext[currentStateKey]
this.useStateCalls++
return[value, (newValue) => this.setState({[currentStateKey]: newValue})]
}
render(){
this.useStateCalls = 0
let [fooState, setFoo] = this.useState("foo default")
let [barState, setBar] = this.useState("bar default")
return(
Foo state
Value:
{fooState}
Bar state
Value:
{barState}
Render context state:
{JSON.stringify(this.state)}
)}} ReactDOM.render ( , document.getElementById ('main'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<main id=main>loading or error occurred...</main>
Обратите внимание, что состояние сохраняется в зависимости от порядка вызова хука внутри render. В реальных хуках контекст рендеринга хранится где-то, кроме this.state
, но хуки знают, как его получить, и вам все равно. Кроме того, это всего лишь пример, реальные хуки работают немного по-другому, но концепция та же самая.
*: async
функции и генераторы не запускаются все сразу, а вместо этого возвращают специальный объект, который позволяет функции запускаться в несколько шагов, ожидая или останавливаясь на await
или yield
.