После прочтения исходного кода поговорим о том, как реализованы React Hooks.

внешний интерфейс исходный код Element React.js
После прочтения исходного кода поговорим о том, как реализованы React Hooks.

Новое в React 16.7-альфа:Hooks. Подводя итог, его функция:FunctionalComponentимеютClassComponentфункция.

import React, { useState, useEffect } from 'react'

function FunComp(props) {
  const [data, setData] = useState('initialState')

  function handleChange(e) {
    setData(e.target.value)
  }

  useEffect(() => {
    subscribeToSomething()

    return () => {
      unSubscribeToSomething()
    }
  })

  return (
    <input value={data} onChange={handleChange} />
  )
}

согласно сDanаргумент, дизайнHooksглавным образом для решенияClassComponentНесколько вопросов:

  1. Сложно повторно использовать логику (используйте только HOC или реквизиты рендеринга), что приведет к глубокой иерархии дерева компонентов.
  2. Будет генерировать огромные компоненты (это означает, что в классе должно быть написано много кода)
  3. Компоненты класса сложны для понимания, как методы требуютbind,thisПункт не ясен

Это действительно проблемы, например, если мы используемreact-router+redux+material-ui, вполне вероятно, что какой-либо компонент окажетсяexportКод, который выходит, — фиолетовый соус:

export default withStyle(style)(connect(/*something*/)(withRouter(MyComponent)))

Это 4-уровневый вложенныйHOCкомпоненты

В то же время, если в вашем компоненте много событий, то вашconstructorВ нем может быть фиолетово:

class MyComponent extends React.Component {
  constructor() {
    // initiallize

    this.handler1 = this.handler1.bind(this)
    this.handler2 = this.handler2.bind(this)
    this.handler3 = this.handler3.bind(this)
    this.handler4 = this.handler4.bind(this)
    this.handler5 = this.handler5.bind(this)
    // ...more

  }
}

Хотя последниеclassможно использовать грамматикуhandler = () => {}Приходите к связыванию быстро, но это также решает проблему объявления, и общая сложность все еще существует.

Тогда естьcomponentDidMountа такжеcomponentDidUpdateподписаться на контент вcomponentWillUnmountВ коде отписки в . Самое главное, вClassComponentКод метода жизненного цикла сложно повторно использовать в других компонентах, что приводит к проблеме низкой скорости повторного использования кода.

И естьclassКод трудно сжать для инструментов упаковки, таких как имена методов.

Для получения более подробной информации вы можете перейти наReactConfвидео, тут много говорить не буду,Тема этой статьи — поговорить об этом с точки зрения исходного кода.Hooksкак это достигается

Давайте сначала разберемся с некоторыми основными понятиями

первыйuseStateэто метод, который сам не может хранить состояние

Во-вторых, он бежитFunctionalComponentВнутри само состояние не может быть сохранено.

useStateпринимает только один параметрinitial value, и не вижу ничего особенного. Так как же React получает ранее обновленный при повторном рендерингеstateШерстяная ткань?

Прежде чем приступить к объяснению исходного кода, мы должны сначала установить некоторые понятия:

React Element

JSXПосле перевода этоReact.createElement, он в конце концов возвращаетReactElementобъект, его данные деструктурируются следующим образом:

const element = {
  ?typeof: REACT_ELEMENT_TYPE, // 是否是普通Element_Type

  // Built-in properties that belong on the element
  type: type,  // 我们的组件,比如`class MyComponent`
  key: key,
  ref: ref,
  props: props,

  // Record the component responsible for creating this element.
  _owner: owner,
};

При этом следует отметить, чтоtype, после того как напишем<MyClassComponent {...props} />, его значениеMyClassComponentэтоclass, а не его экземпляр, который создается при последующем рендеринге.

Fiber

Каждый узел будет иметь соответствующийFiberобъект, его данные деструктурируются следующим образом:

function FiberNode(
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
) {
  // Instance
  this.tag = tag;
  this.key = key;
  this.elementType = null;  // 就是ReactElement的`?typeof`
  this.type = null;         // 就是ReactElement的type
  this.stateNode = null;

  // Fiber
  this.return = null;
  this.child = null;
  this.sibling = null;
  this.index = 0;

  this.ref = null;

  this.pendingProps = pendingProps;
  this.memoizedProps = null;
  this.updateQueue = null;
  this.memoizedState = null;
  this.firstContextDependency = null;

  // ...others
}

Здесь нужно обратить вниманиеthis.memoizedState,этоkeyОн используется для хранения узла, окончательно полученного в последнем процессе рендеринга.stateДа, каждый раз, когда вы выполняетеrenderПеред методом React рассчитает последнюю версию текущего компонента.stateзатем назначьтеclassэкземпляр, затем позвонитеrender.

Поэтому многие студенты, которые не очень хорошо разбираются в принципах React, узнают о React.ClassComponentнеправильно понять, подуматьstateа такжеlifeCycleОни все называются сами по себе, потому что мы унаследовалиReact.Component, в нем должно быть много связанной логики. На самом деле, если вам интересно, вы можете проверить это.ComponentИсходный код, вероятно, более 100 строк, очень прост. Итак, в РеактеclassЭто просто носитель, что облегчает понимание, когда мы пишем компоненты.classзакрыты

принцип

Зная вышеизложенные основы, дляHooksПринцип того, почему компоненты без состояния могут быть сохранены, лучше понят.

Предположим, у нас есть этот фрагмент кода:

function FunctionalComponent () {
  const [state1, setState1] = useState(1)
  const [state2, setState2] = useState(2)
  const [state3, setState3] = useState(3)
}

Первый взгляд на картинку

react-hooks

в нашей реализацииfunctionalComponent, когда первое выполнениеuseState, он будет соответствоватьFiberна объектеmemoizedState, который изначально был предназначен для храненияClassComponentизstate, потому что вClassComponentсерединаstateпредставляет собой цельный объект, поэтому его можно комбинировать сmemoizedStateПереписка один на один.

Но когдаHooks, React не знает, сколько раз мы вызывалиuseState, так что экономияstateВ этом вопросе React придумал более интересное решение, которое заключается в вызовеuseStateустановить послеmemoizedStateВышеупомянутый объект выглядит следующим образом:

{
  baseState,
  next,
  baseUpdate,
  queue,
  memoizedState
}

мы зовем егоHookобъект. Здесь нам следует больше всего беспокоитьсяmemoizedStateа такжеnext,memoizedStateиспользуется для записи этогоuseStateдолжен вернуть результат, аnextуказывая на следующий разuseStateСоответствующий объект `Hook.

То есть:

hook1 => Fiber.memoizedState
state1 === hook1.memoizedState
hook1.next => hook2
state2 === hook2.memoizedState
hook2.next => hook3
state3 === hook2.memoizedState

каждый вFunctionalComponentназывается вuseStateбудет соответствующийHookОбъекты, они хранятся в формате данных в виде связанного списка в порядке выполнения.Fiber.memoizedStateначальство

Вот в чем дело: потому что так делаетсяstateхранение, такuseState(включая другие хуки) должны быть вFunctionalComponentобъявляется в корневой областиifили объявлен в цикле, например

if (something) {
  const [state1] = useState(1)
}

// or

for (something) {
  const [state2] = useState(2)
}

Основная причина в том, что вы не можете гарантировать, что эти условные операторы каждый раз будут выполняться одинаковое количество раз., то есть если мы первый раз создадимstate1 => hook1, state2 => hook2, state3 => hook3После такой переписки следующая казнь полагаетсяsomethingусловия не выполняются, что приводит кuseState(1)не выполняется, то запуститеuseState(2)когда вы получаетеhookобъектstate1, то вся логика перепутана,Так что это условие необходимо соблюдать!

setState

сказано вышеHooksсерединаstateКак сохранить, тогда поговорим о том, как обновитьstate

вызов, который мы называемuseStateСпособ возврата фиолетовый:

var dispatch = queue.dispatch = dispatchAction.bind(null, currentlyRenderingFiber$1, queue);
return [workInProgressHook.memoizedState, dispatch];

Вызов этого метода создаетupdate

var update = {
  expirationTime: _expirationTime,
  action: action,
  callback: callback !== undefined ? callback : null,
  next: null
}

здесьactionэто то, что мы называемsetState1переданное значение, и этоupdateбудет добавлено кqueue, потому что одновременно может быть несколько вызововsetState1Опустошение (связанное с пакетным обновлением React, у меня будет возможность поговорить об этом позже).

Собрав все этоupdateПосле этого он будет назначен один разReactОбновление, в процессе обновления, обязательно будет внедрено в нашуFunctionalComponent, то соответствующийuseState, и тогда мы получимHookобъект, он спасqueueОбъект указывает, какие обновления существуют, а затем обновляет их по очереди, чтобы получить последниеstateСохранить какmemoizedStateвверх и обратно, наконец достигнувsetStateЭффект.

Суммировать

На самом деле, сClassComponentпочти то же самое, только потому, чтоuseStateразделить один объектstate, поэтому вам нужно сохранять данные относительно уникальным способом, и будут определенные правила и ограничения.

Но эти условия никак нельзя скрытьHooksСвет, который он имел в виду, был слишком велик, чтобы позволитьReactэтофункциональное программированиеФреймворки Paradigm наконец-то избавились от неудобной сцены использования классов для создания компонентов. На самом деле существование классов действительно не имеет смысла, напримерPuerComponentСейчас есть соответствующиеReact.memoЧтобы сделать функциональные компоненты можно добиться того же эффекта.

Наконец, поскольку мы действительно хотим распространять исходный код, он будет включать в себя некоторый другой контент исходного кода, такой какworkInProgress => currentконверсия,expirationTimeЗадействованное планирование и т. д. заставят всех не понять основную часть этой статьи.Hooks, поэтому после того, как я написал полный анализ исходного кода, я обобщил эту статью и выпустил ее отдельно. Надеюсь, это поможет вам лучше понятьHooks, и его можно смело использовать в реальных разработках.

Потому что: это действительно полезно! ! !

Уведомление

В настоящее времяreact-hot-loaderне может иhooksиспользовать вместе,Подробности, так что вы можете подождать до официального релиза.

Я Джоки, фронтенд-инженер, который фокусируется на навыках React и глубоком анализе React определенно является фреймворком, который чем больше вы изучаете, тем больше вы чувствуете, что его дизайн сложный и думает наперед. Подписывайтесь на меня, чтобы получать последние новости о React и самые глубокие знания о React.Больше статей здесь