Заставьте волну крючков, на этот раз давайте сменим фокус

внешний интерфейс React.js
Заставьте волну крючков, на этот раз давайте сменим фокус

первыйhooksОна запущена давно, и все, должно быть, более-менее ею пользовались или разбирались.hooks, я не знаю, будет ли у меня такое же чувство, как у меня, то естьhooksОн очень прост в использовании, но это всегда похоже на какое-то волшебство, не очень понятно, как это реализовано внутри, с ним сложно обращаться, поэтому я думаю, что его действительно сложно освоить.hooks, вы должны начать с понимания его внутреннего принципа, затем поговорить о его использовании и попытаться установить тонкий путь от принципа к использованию.

Крючки меняют оранжевый тренд функциональных компонентов

перед крючками

Генетическое ограничение функциональных компонентов

Функциональные компоненты можно грубо рассматривать как компоненты класса.renderфункция, то есть возвратjsxтем самым создавая виртуальныйdomФункция.

Компоненты класса имеютthis, может иметь свои собственные методы экземпляра, переменные, чтобы было легко реализовать различные функции, такие какstateИ функция жизненного цикла, каждый рендеринг можно рассматривать как «однажды» самость, которая постоянно меняется и имеет непрерывность.

С другой стороны, функциональные компоненты не могут быть продолжены, и каждый рендеринг — это «новое» Я. Это «генетическое ограничение» функциональных компонентов, что-то вроде осьминога.

«Небольшая разница» между функциональными компонентами и компонентами класса

Прежде всего, компонент может быть написан в двух версиях с компонентами класса и компонентами функций, верно?

Компонент класса:

class CompClass extends Component {

 showMessage = () => {
 	console.log("点击的这一刻,props中info为 " + this.props.info);
 };

 handleClick = () => {
	 setTimeout(this.showMessage, 3000);
	 console.log(`当前props中的info为${this.props.info},一致就说明准确的关联到了此时的render结果`)
 };

 render() {
 	return <div onClick={this.handleClick}>
		 <div>点击类组件</div>
	</div>;
 }
}

Компонент функции:

function CompFunction(props) {
	
	const showMessage = () => {
		console.log("点击的这一刻,props中info为 " + props.info);
	};
 
	const handleClick = () => {
		setTimeout(showMessage, 3000);
		console.log(`当前props中的info为${props.info},一致就说明准确的关联到了此时的		render结果`)
	};

return <div onClick={handleClick}>点击函数组件</div>;
}

Это означает, что два разных написания эквивалентны, верно?

ответ:Обычно эквивалентны, но бывают случаи, когда они отличаются,Например

export default function App() {
 const [info, setInfo] = useState(0);
 return (
	 <div>
		 <div onClick={()=>{
		 	setInfo(info+1)
		 }}>父组件的info信息>> {info}</div>
		 <CompFunction info = {info}></CompFunction>
		 <CompClass info = {info}></CompClass>
	 </div>
 );
}

Это видно из кода:

  1. в компонентеApp, есть состояниеinfoЕго начальное значение равно 0, и его можно изменить, щелкнув
  2. CompFunctionа такжеCompClassотображаются как дочерние компоненты, и оба принимают родительские компонентыinfoкак параметр,
  3. Оба компонента имеют обратный вызов клика, который запускает 3-секундную задержку после клика.setTimeoutа затем поставить из родительского компонентаAppполучено вinfo,logпублично заявить

Затем сделать его:

  1. просто быстрый щелчокCompFunctionа такжеCompClass, чтобы вызвать его внутреннююsetTimeoutи подождав 3 секунды, посмотреть на отпечаток из родительского компонентаAppполучено вinfoИнформация
  2. Затем щелкните родительский компонент, чтобы изменитьinfo, пока он меняется, скажем, становится 5.

(Рекомендуется попробовать.)

результат:

  1. функциональный компонентCompFunctionвыведет: 0
  2. компонент классаCompClassвыведет: 5

Результаты разные.По логике они должны быть эквивалентны.Почему они разные?

объяснять:

При выполнении функционального компонента будет сформировано замыкание, которое можно визуально выразить каквизуализировать результат, которая включает в себяprops, а также включена функция обработчика события клика, поэтому независимо от того, выполняется ли оно немедленно или с задержкой, оно должно совпадать с моментом запуска выполнения.визуализировать результат(можно также понимать как снимок того момента). Итак, функция обратного вызоваshowMessageдолженlogвнеinfo, который должен быть моментом возникновения событияrenderв результатахinfo, что равно "1", независимо от того, как изменится внешняя информация.

И компонент класса выведетinfoПоследнее значение , равное "5".

В заключение:

Эта «небольшая разница» называетсяcapture value

Содержимое каждого рендеринга будет формировать снимок и сохраняться, поэтому при изменении состояния и рендеринга формируется N состояний рендеринга, и каждое состояние рендеринга имеет свои собственные фиксированные реквизиты и состояние.

classКомпонентам это сделать немного сложно, ведь этот сыр движет React.

capture valueЭто палка о двух концах, но не важно, что есть решение (подробнее об этом позже)

после крючков

Хуки делают эту функцию «рендеринга» усовершенствованной.

если вhooksРаньше функциональные компоненты имели некоторые «недостатки», и их уникальности было недостаточно, чтобы противопоставить их классовым компонентам, но когдаhooksПосле прихода оранжевый тренд будет другим.render"Функция запускается сразу.

Крючки помогают функциональным компонентам сломать генетический замок.

Мы уже говорили, что самый большой недостаток функциональных компонентов — это «Повторяется снова и снова, не могу продолжать", трудно заставить его иметь те же возможности, что и компоненты класса, такие как использование функций состояния и жизненного цикла, и сегодняhooksБлаго, хорошо разбили оковы, сдерживаемые классовыми составляющими.

Итак, в понимании того, как использоватьhooksПрежде всего лучше понять, как функциональные компоненты имеют непрерывность, поэтому используйтеhooksПросто «имейте спектр», иначе вы почувствуетеhooksВезде есть черная магия, так что это не очень "надежно".

Чтобы понять тайну продолжения Hooks, вам, возможно, придется знать Fiber

Нет непрерывности, не говоря уже о других вещах, настоящий большой парень за кулисами, который действительно обеспечивает непрерывность функциональных компонентов, на самом делеFiber, чтобы иметь хорошее представление о том, как React реализует так многоhooks,ТакFiberВы не можете обойти это, но научитесьFiberНе старайся слишком сильно,нажмите до, я постараюсь объяснить как можно проще, наша цель - лучше понять и использоватьHooks, Ведь чтобы есть пельмени, не обязательно уметь их делать.

Структура волокон

type Fiber = {

 // 函数组件记录以链表形式存放的hooks信息,类组件存放`state`信息
 memoizedState: any,

 // 将diff得出的结果提交给的那个节点

 return: Fiber | null,

 // 单链表结构 child:子节点,sibling:兄弟节点

 child: Fiber | null,

 sibling: Fiber | null,
 
 ...


 // 每个workinprogress都维护了一个effect list(很复杂,不会也不耽误我们吃饺子)

 nextEffect: Fiber | null,

 firstEffect: Fiber | null,

 lastEffect: Fiber | null,

 ...

}

Происхождение волокна

Как именно React отображает элемент.

Во-первых, этот процесс называется «примирение», и согласование можно условно разделить на два этапа.

  1. reconciliation: Получить измененный результат с помощью diff.
  2. commit: применить изменение к экрану (side effectто есть побочные эффекты, такие какdomдействовать).

reconciliationасинхронный,commitявляются синхронными.

Как React реализовал сверку перед файбером

Создайте новый виртуальный дом с нуля, т.е.vdom, со старымvdomсделать сравнение, чтобы получитьdiffВ результате этот процесс является рекурсивным, который нужно сделать за один раз и не может быть остановлен, таким образом, если JavaScript занимает основной поток в течение длительного времени, он будет блокировать отрисовку экрана, который очень сильно застрял. .

Потому что JavaScript работает в основном потоке браузера, который выполняется вместе с расчетами стилей, макетом и, во многих случаях, отрисовкой. Если JavaScript выполняется слишком долго, он блокирует эти другие задания, что может привести к пропущенным кадрам.

(цитируетсяOptimize JavaScript Execution)

Таким образом, можно сказать, что старый способ выявил две проблемы:

  • Перемещение сверху вниз без остановки.
  • Длительное выполнение React задерживает работу браузера.

vdom эволюционирует в Fiber

FiberМожно понять, что все вышеперечисленноеreconciliationРабота разделяется, а затем объединяется в связанный список, становясь единицей задачи, которую можно прервать/приостановить/возобновить. и в сочетании с предоставленным браузеромrequestIdleCallbackAPI (интересно знать) для совместной работы.

Ядром Fiber является реализация циклического алгоритма планирования задач, основанного на приоритете и requestIdleCallback.. (Ссылка: волокно-примиритель)

Грубо говоря: просто тарелка лапши и пара палочек. Раньше, когда React ел, браузер мог только наблюдать за этим. Теперь он становится React и откусывает и меняет браузер, чтобы откусить, и это становится гармоничным.

Fiberпросто согласноvdomразделить, аvdomУзел соответствуетFiberузел, который, наконец, формирует структуру связанного спискаfiber tree, как показано на рисунке:

Image

дочерний: указатель на дочерний узел sibling: указатель на родственный узел return: Отправить результат изменения (effectList) на указанный целевой узел (не отмечен на рисунке, ниже будет динамическая демонстрация)

такFiber treeможно нарезатьvdom treeНе слишком.

ТакvdomОн все еще существует?

Я долго думал над этим вопросом, пожалуйста, простите меня, что я не видел исходный код в этой области.Теперь я придумал объяснение, которое я могу принять, и логика непротиворечива, проконсультировавшись с числом связанных статей:

FiberПосле выхода,vdomРоль состоит в том, чтобы просто построить как планFiberДерево.

эм~, Dragon Ball знаком, верно?vdomВроде раньше было достаточно Super Saiyan 1, а сейчас уже мало, и он эволюционировал в Super Saiyan 2, т.е.Fiber.

Как работает волокно

Сначала я уже знаю,Fiber treeпредставляет собой структуру связанного списка, React обрабатывает каждыйFiberЕдиница работы возвращает управление браузеру через некоторое время, чтобы взаимодействовать и сделать страницу более плавной.

Чтобы понять, как функционируют компонентыпреемственностьОтвет скрыт в этомрабочий циклсередина.

Исследуйте рабочий цикл

Для того, чтобы избавиться от сонливого и долгого анализа исходного кода, можно попробовать сначала просто его понять.workLoop.

Прежде всего, что такое Loop?

Рабочий блок,Прямо сейчасwork.

workЕго можно условно разделить на:

  • beginWork:начинай работать
  • completeWork: работа сделана

Затем объедините предыдущее дерево Fiber и посмотрите

Image

Тогда взгляните на общий процесс работы:

Image

Потом через анимацию у меня есть предварительное понимание всегоworkLoopПроцесс потока кратко описывается следующим образом:

  1. верхrootВниз, подузел потокаb1
  2. b1 начинаетсяbeginWork, рабочая цель обрабатывается в соответствии с ситуацией, и получается результат изменения (effectList), а затем рассудить, есть ли дочерние узлы, и завершить работу без этогоcompleteWork, а затем перейти к родственному узлуb2
  3. b2Начните работу, а затем рассудите, что есть дочерние узлыc1, затем течь вc1
  4. c1работа сделана,completeWorkполучатьeffectList, и отправить наb2
  5. потомb2завершить работу, передатьb3,Такb3Просто следуйте по этому пути, спускайтесь вниз и, наконец, выполняйте до конца.d2
  6. Наконец, с маршрутом курсора интегрируйтеeffectList, наконец, прибылRootУзел, Фаза 1 -reconciliationКонец, готов к вождениюCommitсцена

Делая шаг вперед, ответ на «продолжение» вот-вот появится.

У нас есть общее представление оworkLoop, но это все еще не может объяснить, как функциональный компонент «продолжается», нам все еще нужно понять более глубоко, а затем разбить его в деталяхworkLoop, что на самом деле выглядит так:

test.gif

(В анимации «текущее» и «резервное» — это одно, чтобы было легче понять: «построение wip-дерева — это взять текущее дерево как можно больше». В конце анимации текущее описывается резервным сервером, чтобы показать, что текущее дерево используется в качестве резервного)

Опишите процесс:

  1. согласно сcurrent fiber treeклонироватьworkinProgress fiber tree, по одному на клонworkinProgress fiberповторно использовать как можно большеfiberузел (ранееcurrent fiber)
  2. При построении полногоworkinProgress fiber treeкогда,current fiber treeвернется в качестве резервной копииfiberдерево узлов, затемworkinProgress fiber treeисправится и станет новымcurrent fiber tree
  3. Затем собранные результаты изменений (effect list)новыйcurrent fiber tree, Отправитьcommitэтапе, тем самым обновив экран

Несколько моментов, которые я должен отметить:

  • current fiber treeрешать, что отображать на главном экране,workinProgress fiber treeПосле завершения производства станьте следующимcurrent fiber tree
  • ПостроитьworkinProgress fiber treeпроцесс, то естьdiffпроцесс, основная работа происходит вworkinProgress fiber, если есть изменения, он будет поддерживатьeffect list, когда работа выполнена, он отправляет сетку вreturnузел указал.
  • отречьсяcurrent fiber treeКак резервная копия, действует как сборкаworkinProgress fiber treeсырья, максимизировать экономию производительности и так далее.
  • собралeffect listСосредоточьтесь только на узлах, которые изменились, и расположите их от самого глубокого к переднему, что соответствует порядку обновления от дочерних узлов к родительским узлам.

Двойное волокнистое дерево — ключ к проблеме

Есть два этапа:

  • Первый рендеринг: сразу первыйcurrent fiber treeстроить из
  • Обновление рендеринга: продолжениеcurrent fiber treeПостроитьworkinProgress fiber tree

В трансформации должно быть продолжение

фаза обновления, втораяfiberДеревья как близнецы,current fiberа такжеworkinProgress fiberмеждуalternateЭтот указатель ассоциирован, то есть его можно обрабатыватьworkinProgress fiberво время работы вы можете получитьcurrent fiberинформацию, если она не является совершенно новой, то воссоздать ее.

каждая сборкаworkinProgress fiber, если этоfiberСоответствующий узел является функциональным компонентом и может быть доступен черезalternateполучатьcurrent fiber, то продолжим продолжение, сущность, несущая продолжение, естьcurrent fiberизmemoizedStateэто свойство

Суть продолжения все тамmemoizedState

при первом рендере

Выполняем наши функции в функциональном компоненте по очередиhooks, для каждого вида исполненияhooks, создаст соответствующий типhookОбъект, используемый для хранения информации.

  • useState соответствует информации о состоянии
  • useEffect соответствует объекту эффекта
  • useMemo соответствует кэшированным значениям и deps
  • useRef соответствует объекту ref
  • ...

Эта информация будет храниться в виде связанного списка вcurrent fiberизmemoizedStateсередина

При обновлении рендера

Каждая сборка соответствуетфункциональный компонентизworkinProgress fiber, начнется с соответствующегоcurrent fiberпродолжать этоИнформация о хуках хранится в структуре связанного списка.

Например, функциональный компонент:

export default function Test() {
 const [info1, setInfo1] = useState(0);
 useEffect(() => {}, [info1]);
 const ref = useRef();
 const [info2, setInfo2] = useState(0);
 const [info3, setInfo3] = useState(0);
 return (
	 <div>
		<div ref={ref}> {`${info1}${info2}${info3}`}</div>
	 </div>
 );
}

ТакhooksПродолжение такое:

hooksList.jpg

Продолжить по порядку связанного списка, если один изhooksЗаписанный в условном операторе, код выглядит следующим образом:

export default function Test() {
 const [info1, setInfo1] = useState(0);
 let ref;
 useEffect(() => {
 setInfo1(info1+1)
 }, [info1]);
 if(info1==0){
 	ref = useRef();
 }
 const [info2, setInfo2] = useState(0);
 const [info3, setInfo3] = useState(0);
 return (
	 <div>
		<div ref={ref}> {`${info1}${info2}${info3}`}</div>
	 </div>
 );
}

Тогда порядок продолжения будет нарушен, а информация потеряется, например:

QQ截图20211121210010.jpg

Так что это неhooksПричина написана в условном выражении

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

Те всей семьи на крючках

Узнайте о ловушках захвата ценности и закрытия

capture valueКак следует из названия, «захваченное значение», функциональный компонент выполняется один раз и генерирует замыкание, точно так же, как моментальный снимок. Это перекликается с «коррелированными результатами рендеринга» или «моментальными снимками в тот момент», которые мы проанализировали выше.

когдаcapture valueвстретитьhooksПроблемы возникают из-за использования «устаревших моментальных снимков», которые называютсяловушка закрытия.

Но как бы это ни называлось, корневой узел — это проблема «просроченного закрытия».useEffectПроблема экспозиции наиболее очевидна.

Давайте сначала возьмем 🌰:

let B = (props) => {
  const { info } = props;
  const [count,setCount] = useState(0);
  useEffect(()=>{
    setInterval(()=>{
      //这才是dispatch函数正确的使用方式
      setCount((old)=>{
        return old+1;
      })
    },1000)
  },[])
  useEffect(()=>{
      setInterval(()=>{
          console.log("info为:"+info+" count为:"+count)
      },1000)
  },[])
  return <div></div>
}


let A = (props) => {
  const [info,setInfo] = useState(0);
  useEffect(()=>{
    setInterval(()=>{
      //这才是dispatch函数正确的使用方式
      setInfo((old)=>{
        return old+1;
      })
    },1000)
  },[])
  return <div>
    <B info={info}></B>
    {info}
    </div>
}

export default function App() {
  return (
    <div>
      <A>
      </A>
    </div>
  );
}

Такой журнал всегда былinfo:0 count:0, по-видимому, используя данные из связанного «снимка с истекшим сроком действия».

Решение:

пройти черезuseRefполучатьrefобъект

refСтруктура такая:

{
	current:null
}

Мы назначаем данные, которыми необходимо управлять,current, стоит отметить, что вы можете назначить толькоcurrent,refОбъект не поддерживает расширение.

Затем переписываем код:

let B = (props) => {
  const { info } = props;
  const [count,setCount] = useState(0);
  const refInfoFromProps = useRef();
  const refCountFromProps = useRef();
  refInfoFromProps.current = info;
  refCountFromProps.current = count;
  useEffect(()=>{
    setInterval(()=>{
      //这才是dispatch函数正确的使用方式
      setCount((old)=>{
        return old+1;
      })
    },1000)
  },[])
  useEffect(()=>{
      setInterval(()=>{
          console.log("info为:"+refInfoFromProps.current+" count为:"+refCountFromProps.current)
      },1000)
  },[])
  return <div></div>
}

Это позволяет каждый раз получать доступ к последним данным.

Конечно, есть много других способов, таких как использованиеuseReducer, вы можете изучить его, если вам интересно.

Вещь useState

Это настроено на обновление?

Сначала посмотрите на этот код

let A = ((props) => {
  const [count,setCount] = useState({a:1})
  
  return <div onClick={()=>{
    count.a = Date.now();
    setCount(count)
  }}>
  	测试是否刷新
  </div>
})

Срабатывает, когда я нажимаюsetCount, пожалуйста, обновите?

ответне обновлять, потому что когда мы используем React, мы всегда должны напоминать себе, что:

неизменное значение, неизменное значение, неизменное значение

В приведенном выше коде есть две основные проблемы:

  • Изменение состояния напрямую нарушает правила неизменяемых значений, вы должны пройтиObject.assignИли оператор распространения, чтобы воссоздать объект для установки.
  • Внутри React выполнит поверхностное сравнение входящих параметров.Данные ссылочного типа сравнивают адрес, на который они указывают, а не содержимое.Помните, легкое содержимое становится бесполезным.

Правильное написание

let A = ((props) => {
  const [count,setCount] = useState({a:1})

  return <div onClick={()=>{
    setCount({...count,a:Date.now()})
  }}>
    测试是否刷新
  </div>
})

useState — это не то же самое, что setState

useStateизsetфункции и компоненты классаsetStateНейминг очень похож, создаст иллюзию, что они одинаковые, но это не так, первое на самом деле одноdispath,потому чтоuseStateВнутренне на основеuseReducerосуществленный. И это не должно быть названоset***, вы можете назвать его как хотите.

Стоит отметить три отличия:

setState:

  1. Второй параметр — функцияВы можете получить обратный вызов после того, как установлена ​​настройка значения состояния, мы можем получить новейшее значение состояния в этом.
  2. setState имеет неглубокую функцию слияния, например, состояние{a:1,b:2,c:{e:0}},setState({c:{f:0},d:4}),stateбудут объединены в{a:1,b:2,c:{f:0},d:4}
  3. setStateУстановка состояния вызовет обновление, даже если установлено одно и то же значение, если толькоPureComponentОсознать, чтобы решить

setфункция

  1. нет второго параметра, но можно использоватьuseEffectСочетание тоже хорошее
  2. нет функции слияния, что установлено то что. . . , но также можно оптимизировать его самостоятельно.
  3. Установка одного и того же состояния не приведет к обновлению, который не требует настройки.

Далее подробно обсуждается интересный вопрос.

useStateизsetЯвляется ли функция синхронной или асинхронной?setStateЭто синхронно или асинхронно?

Ответы на удивление последовательны, а именно:

Асинхронно большую часть времени, синхронно иногда

Когда именно синхронизируется? то есть

Если это обработка событий, инициированная React (например, обработка событий, инициированная onClick), вызов setState не будет синхронно обновлять this.state, а другие вызовы setState будут выполнять this.state синхронно.

Если вы мне не верите, посмотрите на код:

export default function Test() {
  const [info1, setInfo1] = useState(0);
  const [info2, setInfo2] = useState(0);
  const ref1 = useRef();
  const ref2 = useRef();
  ref1.current = info1;
  ref2.current = info2;
  useEffect(() => {
    setInfo1(ref1.current + 1);
    setInfo1(ref1.current + 1);
    setInfo1(ref1.current + 1);
    console.log("info1:"+ref2.current); // info1:0
    setTimeout(() => {
      setInfo2(ref2.current + 1);
      setInfo2(ref2.current + 1);
      setInfo2(ref2.current + 1);
      console.log("info2:"+ref2.current);// 同步输出 info2:3
    });
  }, []);
  return <div>{info1}</div>;
}

Выходной журнал:info1:0 info2:3

ТакuseStateизsetфункция такая же какsetStateТо же самое, так что давайте поговоримuseStateизsetКогда функция асинхронная или синхронная, вы знаете, что сказать.

Вещь useEffect

useEffectЕсть два параметра, одинeffectВыполненная функция обратного вызова, один зависимый массив

в то же времяuseEffectВы можете написать более одного, так что вы можетеРаздельно по бизнесу, делатьразделение интересов

Жизненный цикл

useEffectЭто важное средство для функциональных компонентов для реализации функций жизненного цикла.

Жизненные циклы, которые можно моделировать:

  • componentDidMount
  • componentWillUnmount
  • componentDidUpdate

код показывает, как показано ниже:

  useEffect(() => {
    // 相当于 componentDidMount
    return () => {
      // 相当于 componentWillUnmount
    }
  }, [])
 
  useEffect(() => {
    // 相当于 componentDidUpdate
  })

Время выполнения функции очистки useEffect

первыйчеткая функцияВозможны два случая исполнения:

  • Один при удалении, это хорошо известно.
  • Один из них, когда эффект повторно выполняется, тоже будет реализовано, всем стоит обратить на это внимание, легко быть неаккуратным

Тогда посмотрите на этот код

useEffect(
    () => {
        ...
        return () => {
          ...
          console.log("test")
        }
    },
    [flag]],
  )

Спросите: когдаflagотtrueнастраиватьfalse,этоreturnбудет выполняться функция очистки?

ответ:воплощать в жизнь

Сильнее

useEffect(
    () => {
      if (flag) {
        ...
        return () => {
          ...
          console.log("test")
        }
      }
    },
    [flag]],
  )

Будет ли он реализован?

ответ:воплощать в жизнь

Можно запомнить железное правило:

При повторном выполнении эффекта последний эффект будет очищен.

Разница между useEffect и useLayoutEffect

useEffectасинхронный,useLayoutEffectсинхронно Так называемый асинхронный метод заключается в использованииrequestIdleCallback, который выполняет переданный во время простоя браузераcallback, то есть продолжить обработку js логики В большинстве случаев разницы нет, но когда есть трудоемкая логика,useLayoutEffectблокировка рендеринга

Сначала опубликуйте фрагмент кода, это интересный пример, который я нашел в Интернете.

function TestEffectApi() {
  const [lapse, setLapse] = useState(0)
  const [running, setRunning] = useState(false)

  useEffect(
    () => {
      if (running) {
        const startTime = Date.now() - lapse
        const intervalId = setInterval(() => {
          setLapse(Date.now() - startTime)
        }, 2)
        console.log(intervalId)
        return () => {
          clearInterval(intervalId)
        }
      }
    },
    [running],
  )

  function handleRunClick() {
    setRunning(r => !r)
  }

  function handleClearClick() {
    setRunning(false)
    setLapse(0)
  }

  return (
    <div>
      <label>{lapse}ms</label>
      <button onClick={handleRunClick}>
        {running ? '暂停' : '开始'}
      </button>
      <button onClick={handleClearClick}>
        暂停并清0
      </button>
    </div>
  )
}

Из кода видно, что когда я нажимаю кнопку "пауза и сброс 0", мы устанавливаем два состояния, одноrunningа такжеlapse, первый управляет работой таймера, второй — отображением данных, а ожидание после нажатия такое: таймер выключен, а отображаемые при этом данные — «0», но фактическая ситуация такова, что иногда отображается не "0"

причина:

потому чтоuseEffectявляется асинхронным, когда он установленrunningТаймеры выключения и настройкиlapseПри значении "0" таймер не выключается в первый раз, а случайно возникает ситуация:lapseОн был установлен на ноль, и таймер будет выключен, прежде чем он будет выключен.Эта проблема возникает при повторном выполнении. при синхронном выполненииLayoutEffectнет такой проблемы

В приведенном выше примереuseEffectа такжеLayoutEffectРазница должна быть очевидна.

useRef действительно полезен

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

не только сохранитьdomУзлы также могут сохранять другие данные, такие как сохранение внешних данных в упомянутой выше проблеме закрытий с истекшим сроком действия.propа такжеstate.

К тому же есть интересное применение, то есть его можно совмещать сforwardRefа такжеuseImperativeHandleЭти два API позволяют функциональным компонентам вести себя как компоненты класса.выставить функциюдля других узлов

код показывает, как показано ниже:

let A = forwardRef((props, ref) => {
  useImperativeHandle(ref, () => {
    return {
      test: () => {
        console.log("123")
      }
    }
  })
  const [info, setInfo] = useState(0);
  return <div>
    {info}
  </div>
})

export default function App() {
  const ref = useRef(null);
  return (
    <div onClick={() => {
      ref.current.test()
    }}>
      <A ref={ref}>
      </A>
    </div>
  );
}

Создать родительский компонентref,Затем отдайте его дочернему компонентуAНо дочерний компонент является функциональным компонентом и не может быть задан напрямую, тогда используйтеforwardRefПосле упаковки этого HOC вы можете получить его и использовать в качестве продолжения.propsПосле этого передается второй параметр получить ссылку какuseImperativeHandleПервый параметр , а второй параметр — это функция, которая возвращает объект, заполненный открытыми данными.

дляforwardRefЭтот Hoc я считаю совершенно ненужным, я переделаю код

let A = ((props) => {
  const {testRef} = props;
  useImperativeHandle(testRef, () => {
    return {
      test: () => {
        console.log("ok")
      }
    }
  })
  const [info, setInfo] = useState(0);
  return <div>
    {info}
  </div>
})

export default function App() {
  const ref = useRef(null);
  return (
    <div onClick={() => {
      ref.current.test()
    }}>
      <A testRef={ref}>
      </A>
    </div>
  );
}

Так же можно написать и так, это выглядит намного лаконичнее и предназначено только для справки.

useContext может в определенной степени заменить сторонние библиотеки управления данными.

Сначала опубликуйте полный исполняемый код

import {
  createContext,
  useContext,
  useReducer,
} from "react";
export const TestContext = createContext({})
const TAG_1 = 'TAG_1'

const reducer = (state, action) => {
  const { payload, type } = action;
  switch (type) {
    case TAG_1:
      return { ...state, ...payload };
      dedault: return state;
  }
};

export const A = (props) => {
  const [data, dispatch] = useReducer(reducer, { info: "本文作者" });
  return (
    <TestContext.Provider value={{ data, dispatch }}>
      <B></B>
    </TestContext.Provider>
  );
};

let B = () => {
  const { dispatch, data } = useContext(TestContext);
  let handleClick = ()=>{
    dispatch({
        type: TAG_1,
        payload: {
          info: "闲D阿强",
        },
      })
  }
  return (
    <div>
      <input
        type="button"
        value="测试context"
        onClick={handleClick}
      />
      {data.info}
    </div>
  );
};

Используемые API:

  • createContext
  • useReducer
  • useContext

Шаги для реализации:

  • функциональный компонент А

    1. использоватьcreateContextAPI создатьTestContext, а затем используйтеProvider
    2. затем используйтеuseReducerAPI создатьreducer,Будуreducerвернутьdata, dispatch,пройти черезProviderделиться
  • функциональный компонент В

    1. используется внутри негоuseContextapi и передать в созданныйTestContext, чтобы получитьdata,dispatch
    2. использоватьdataсерединаinfoЗначение как отображение, вызванное событием кликаdispatchВнесите изменения, чтобы убедиться, что отзыв правильный

em~, в настоящее время он может в определенной степени заменить библиотеку управления данными, да, в определенной степени.

Пользовательские хуки отличаются от предыдущих инкапсулированных функций инструмента.

Нестандартный крючок, наверное, такой

const useMousePosition = () => {
    const [position, setPosition] = useState({x: 0, y: 0 })
    useEffect(() => {
        const updateMouse = (e) => {
            setPosition({ x: e.clientX, y: e.clientY })
        }
        document.addEventListener('mousemove', updateMouse)
        return () => {
            document.removeEventListener('mousemove', updateMouse)
        }
    })
    return position
}

Я боролся с проблемой, напишите обычайhookОтличается ли это от простой инкапсуляции функции?

Теперь кажется, что ответ да.Что касается того, как отличить, я думаю, что это так:

настроитьhookОтличие от других функций инструмента в том, что вы можете использовать официально предоставленныеhooksи другие настройкиhook, имеет свое состояние, например, обычайhookкак нет возвратаjsxФункция компонента. Конечно, этим преимуществом тоже можно не пользоваться, тогда оно ничем не отличается от обычных функций. . . Но с одной стороны, разделение общей логики и предотвращение дублирования кода имеет много возможностей.

Как оптимизировать производительность функциональных компонентов

memo

memo — это компонент более высокого порядка, и его использование очень простое:

let A = memo((props) => {
  return <div >
    memo测试
  </div>
})

Во многих статьях говоритсяmemoэквивалентноPureComponentЯ не думаю, что я готов понять:

Сами функциональные компоненты имеют наследованиеPureComponentСозданы компоненты класса с аналогичными возможностями Цель памятки должна быть компонентом классаshouldComponentUpdate

например сPureComponentСоздайте компонент класса:

class C extends PureComponent{
  state={
    a:1
  }
  render(){
    return <div onClick={
      ()=>{
        this.setState({
          a:1
        })
      }
    }>测试组件是否刷新</div>
  }
}

Нажмите, чтобы установить переменную состоянияaто же значение1, страница не обновляется, вы используетеComponentСозданный компонент обновляется Но на данный момент он есть у самой функциональной составляющей, поэтому нет необходимости делать это намеренно.

а такжеmemoСамое главное — избегать ненужного обновления функциональных компонентов, что аналогичноshouldComponentUpdateтакой же.

shouldComponentUpdateЭто жизненный цикл, код выглядит следующим образом:

	...
	shouldComponentUpdate (nextProps, nextState) {
      if (nextProps.name === this.props.name) return false
      return true
    }
	...

memoреализация, передавая второй параметрpropsAreEqual, код такой:

let propsAreEqual = (prevProps, nextProps)=>{
	// 根据具体业务判断传入的参数是否相当决定刷新
	// true 表示相等,不刷新,函数组件就不会执行
	// false 表示不等,刷新,就会执行
	return false
}
let A = memo((props) => {
  return <div >
    memo测试
  </div>
},propsAreEqual)

Один называется «shouldComponentUpdate», «Могу ли я обновить»

Один называется "propsAreEqual", "равны ли параметры"

«Равно истине», конечно, «не может обновить ложь», эм~ Они противоположны.

useMemo

Избегайте двойных вычислений, подобных вычисляемым свойствам.

useMemo(()=>{return "计算的值"},[依赖的值])

Обратите внимание на ловушку замыкания, что используется в логике и какая конечная зависимость

useCallback

Прежде всего, функция является ссылочным типом, а также является объектом по своей сути, некоторые функции будут создаваться внутри функционального компонента для организации бизнеса, а также будут передаваться дочерним узлам в качестве параметров.

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

затем используйтеuseCallbackПомните, зависимость не меняется, ссылка на функцию не меняется, ее можно понимать как зависимость конфигурации в ответ на обновлениеuseRef

Это написано следующим образом:

useCallback(() => {}, [依赖的值]);

Обратите внимание на ловушку замыкания, что используется в логике и какая конечная зависимость

Суммировать

Итак, давайте разобраться на наших идеях, мы начинаем сhooksГоворя о компоненте вспомогательной функции, я был очарован волшебством, которым могут продолжаться хуки, а затем исследовал внутренний принцип работы и узнал, что тайна продолжения кроется в этом.fiberСреди них и, наконец, разговор об использованииhooksВ развитии двух или трех вещей постарайтесь установить тонкий путь от принципа к использованию, цель состоит в том, чтобы сначала настроить мышление, чтобы обучение и дополнение были более напористыми, как так называемый первый неудачник. а потом победитель.

Не по теме

Всякий раз, когда я был очарован тонкостью хуков и пошел проверять соответствующую информацию, я сначала был действительно сбит с толку, и я не сразу подумал, насколько хороша статья, поскольку я перечитывал ее неоднократно и начинал отлаживать исходный код React до подтверждают некоторые сомнения. Наконец, обычный кодер, такой как я, едва может почувствовать силу статьи, но это заставило меня задуматься о том, не слишком ли глубока сила статьи, независимо от того, насколько сильна сила, она слаба, если она не могу достучаться до читателей Если продвигаться все дальше и дальше и постепенно терять понимание, то это бесполезно. Я надеюсь, что смогу связать двоих и найти подходящую позицию, чтобы высвободить свою силу, если эта сила сможет достичь как можно большего числа людей, тогда как бы ни была слаба сила, она все равно сильна, так что можно помочь большему количеству людей оценить действительно хорошие статьи.Все хотят быть "нефритом", поэтому я буду "кирпичиком".

Проект отладки исходного кода React, который может отлаживать v16.x и v17.x

Пара хороших статей, которые глубоко вдохновили:

Разберитесь с принципом реактивных хуков в одной статье

Полностью понять React Fiber

Механизм React: Fiber

Говоря о React16 Framework — Fiber