Внимательно прочитайте его, следуйте демо codeandbox или запуститеисходный код, вы познакомитесь с преимуществами, недостатками и использованием различных компонентов реакции,Быть полностью знаком с использованием реактивных крюков, доход должен быть не маленьким 😀😀😀
Контур:
- Реагируйте на использование различных компонентов.
- Что приносят реактивные хуки по сравнению с тем, что было раньше.
- Использование реагирующих хуков.
Эволюция компонентов React
1. Функциональные компоненты (без сохранения состояния)
Функциональный компонент (без сохранения состояния), также называемый компонентом без сохранения состояния, обычно отвечает только за рендеринг.
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
2. Компоненты класса (состояния)
Компонент класса (с отслеживанием состояния), компоненты класса также являются компонентами с отслеживанием состояния, и их также можно назвать компонентами-контейнерами. Как правило, есть логика взаимодействия и бизнес-логика.
class Welcome extends React.Component {
state = {
name: ‘tori’,
}
componentDidMount() {
fetch(…);
…
}
render() {
return (
<>
<h1>Hello, {this.state.name}</h1>
<button onClick={() => this.setState({name: ‘007’})}>改名</button>
</>
);
}
}
3. Компоненты рендеринга
Презентационный компонент, аналогичный функциональным компонентам (без сохранения состояния).
const Hello = (props) => {
return (
<div>
<h1>Hello! {props.name}</h1>
</div>
)
}
**📢 Резюме: **
- Функциональные компоненты должны быть компонентами без состояния, а компоненты отображения обычно являются компонентами без состояния;
- Компоненты класса могут быть компонентами с состоянием или без него;
- Компоненты-контейнеры обычно являются компонентами с отслеживанием состояния.
- Принципы деления сводятся к следующему:Разделяй и властвуй, высокая сплоченность, низкая связанность;
Большинство требований может быть достигнуто за счет комбинации вышеперечисленных компонентов.
4. Компоненты высшего порядка
Higher order components (HOC)
HOC в основном извлекает состояние, извлекает логику повторяющихся управляемых компонентов в компоненты более высокого порядка и передает их управляемым компонентам с новыми реквизитами. Общие компоненты более высокого порядка в библиотеках с открытым исходным кодом: Redux connect, react-router withRouter и т. д.
Class HocFactory extends React.Component {
constructor(props) {
super(props)
}
// 操作props
// …
render() {
const newProps = {…};
return (Component) => <Component {…newProps} />;
}
}
Const Authorized = (Component) => (permission) => {
return Class Authorized extends React.Component {
…
render() {
const isAuth = ‘’;
return isAuth ? <Component /> : <NoMatch />;
}
}
}
// 项目中涉及到的高阶组件
// 主要作用是将所有action通过高阶组件代理到component的Pro上。
import { bindActionCreators } from ‘redux’;
import { connect } from ‘react-redux';
// 所有页面action集合
import * as actions from './actions';
// 缓存actions, 避免render重新加载
let cachedActions;
// action通过bindActionCreators绑定dispatch,
const bindActions = (dispatch, ownProps) => {
if (!cachedActions) {
cachedActions = {
dispatch,
actions: bindActionCreators(actions, dispatch),
};
}
return cachedActions;
};
const connectWithActions = (
mapStateToProps,
mergeProps,
options
) => (component) => connect(
mapStateToProps, bindActions, mergeProps, options
)(component);
export default connectWithActions;
// 类似还有log中间件样子的等等。
Недостатки HOC
- HOC генерирует множество бесполезных компонентов, углубляет иерархию компонентов и влияет на производительность и отладку.
- Несколько HOC вложены одновременно, захватывая реквизиты, имена могут конфликтовать, и невозможно определить, из какого HOC происходят реквизиты.
5. Render Props
Render Props Вы можете думать об этом как о функции обратного вызова в JavaScript.
// 实现一个控制modal visible的高阶组件
class ToggleVisible extends React.Component {
state = {
visible: false
};
toggle = () => {
this.setState({visible: !this.state.visible});
}
render() {
return (
<>{this.props.children({visible, toggle})}</>
);
}
}
//使用
const EditUser = () => (
<ToggleVisible>
{({visible, toggle}) => (<>
<Modal visible={visible}/>
<Button onClick={toggle}>打开/关闭modal</Button>
</>)}
</ToggleVisible>
)
📢 Преимущества
- Повторное использование компонентов не приведет к созданию избыточных узлов, то есть избыточного вложения не произойдет.
- Не беспокойтесь о проблемах с именами PROPS.
6. Составной компонент
Реквизиты, требуемые дочерним компонентом, будут инкапсулированы в родительском компоненте, поэтому нет необходимости передавать все реквизиты при ссылке на дочерний компонент. В основе компонентов композиции лежат два метода: React.Children.map и React.cloneElement.
Например, событие щелчка, требуемое дочерним компонентом, передается родительскому компоненту и инкапсулируется в дочерний компонент через родительский компонент.Многие групповые компоненты ant-design используют этот метод.
class GroupButton extends React.PureComponent {
state = {
activeIndex: 0
};
render() {
return (
<>
{React.Children.map(this.props.children, (child, index) =>
child.type
? React.cloneElement(child, {
active: this.state.activeIndex === index,
onClick: () => {
this.setState({ activeIndex: index });
this.props.onChange(child.props.value);
}
})
: child
)}
</>
);
}
}
// 用法
<GroupButton
onChange={e => {
console.log(“onChange”, e);
}}
>
<Button value="red">red</Button>
<Button value="yellow">yellow</Button>
<Button value=“blue”>blue</Button>
<Button value="white">white</Button>
</GroupButton>
🙊 С чушью покончено, переходим к делу. . .
👍 Реагировать на крючки
Крюк появляется до того, как мультиплексирование логического состояния между компонентами становится затруднительным, требуется реструктуризация элементов ткани решения (HOC, Render Props), а код сложен. React React DevTools Приложение, наблюдаемое в приложении, вы обнаружите, что сборка уровня абстракции другими поставщиками, потребителями, компонентами высокого порядка, реквизитами рендеринга и т. д. образует «вложенный ад».
Сопровождение компонентов становится все более и более сложным, например, логика мониторинга событий должна быть привязана и развязана в разных жизненных циклах, сложная страница componentDidMount содержит много логики, а читабельность кода становится очень плохой.
Компонент this в классе трудно понять, и класс нельзя хорошо сжать, и это сделает горячую перезагрузку нестабильной. Дополнительные сведения см.Официальное введение.
Итак, крючки решают эти проблемы:
- Избегайте адского вложения, улучшайте читаемость.
- Функциональные компоненты легче понять, чем классы.
- Жизненный цикл компонента класса слишком сложен, чтобы компонент функции имел состояние.
- Устраните недостатки HOC и Render Props.
- Пользовательский интерфейс и логику легче разделить.
Ниже описаны официально предоставленные API-интерфейсы хуков один за другим.
1. useState
📢 Функциональные компоненты имеют состояние
const [state, setState] = useState(initialState);
состояние является переменной,setState
Метод изменения значения состояния setState также выполняется асинхронно.
Обновление класса this.setState заключается в том, что состояние объединяется, а setState в useState является заменой.
function Example() {
// 声明一个叫 "count" 的 state 变量
const [count, setCount] = useState(0);
const [obj, setData] = useState();
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
2. useEffect
📢 Забудьте о жизненном цикле, помните о побочных эффектах
useEffect(() => {// Async Action}, ?[dependencies]); // 第二参数非必填
function Hook2() {
const [data, setData] = useState();
useEffect(() => {
console.log("useEffect");
});
return (
<div>
{(() => {
console.log("render");
return null;
})()}
<p>data: {JSON.stringify(data)}</p>
</div>
);
}
Результаты:
В заключение:
- useEffect выполняется после рендеринга.
import React, { useState, useEffect, useRef } from “react”;
function Demo3() {
const [data, setData] = useState();
useEffect(() => {
console.log("useEffect—[]”);
fetch(“https://www.mxnzp.com/api/lottery/common/latest?code=ssq”)
.then(res => res.json())
.then(res => {
setData(res);
});
}, []);
useEffect(() => {
console.log("useEffect ---> 无依赖");
});
useEffect(() => {
console.log(“useEffect 依赖data: data发生了变化”);
}, [data]);
return (
<div>
<p>data: {JSON.stringify(data)}</p>
</div>
);
}
export default Demo3;
Результаты:
В заключение:
- Эффекты выполняются последовательно после рендера.
- При отсутствии каких-либо зависимостей эффекты выполняются по порядку каждый раз после рендеринга.
- Внутреннее выполнение эффекта асинхронно.
- полагаться
[]
можно добиться чего-то вродеcomponentDidMount
эффект, но лучше забыть о жизненном цикле и помнить только о побочных эффектах.
import React, { useState, useEffect, useRef } from "react";
function Demo4() {
useEffect(() => {
console.log(“useEffect1”);
const timeId = setTimeout(() => {
console.log(“useEffect1-setTimeout-2000”);
}, 2000);
return () => {
clearTimeout(timeId);
};
}, []);
useEffect(() => {
console.log("useEffect2");
const timeId = setInterval(() => {
console.log("useEffect2-setInterval-1000");
}, 1000);
return () => {
clearInterval(timeId);
};
}, []);
return (
<div>
{(() => {
console.log(“render”);
return null;
})()}
<p>demo4</p>
</div>
);
}
export default Demo4;
Результаты:
В заключение:
- Функции обратного вызова эффекта выполняются одновременно по порядку.
- Функция обратного вызова эффекта возвращает анонимную функцию, которая эквивалентна
componentUnMount
Функции ловушки обычно удаляют eventListener, очищают timeId и т. д., в основном для предотвращения утечек памяти после удаления компонента.
Подводя итог, можно сказать, что useEffect — это функция-ловушка в функциональном компоненте, которая выполняет функцию обратного вызова всякий раз, когда изменяется зависимость.
3. useContext
Хуки-функции для обмена данными между компонентами
const value = useContext(MyContext);
// MyContext 为 context 对象(React.createContext 的返回值)
// useContext 返回MyContext的返回值。
// 当前的 context 值由上层组件中距离当前组件最近的<MyContext.Provider> 的 value prop 决定。
import React, { useContext, useState } from “react”;
const MyContext = React.createContext();
function Demo5() {
const [value, setValue] = useState("init”);
console.log(“Demo5”);
return (
<div>
{(() => {
console.log("render");
return null;
})()}
<button onClick={() => {
console.log('click:更新value')
setValue(`${Date.now()}_newValue`)
}}>
改变value
</button>
<MyContext.Provider value={value}>
<Child1 />
<Child2 />
</MyContext.Provider>
</div>
);
}
function Child1() {
const value = useContext(MyContext);
console.log(“Child1-value”, value);
return <div>Child1-value: {value}</div>;
}
function Child2(props) {
console.log(‘Child2’)
return <div>Child2</div>;
}
Результаты:
В заключение:
-
Компоненты с useContext всегда будут перерисовываться при изменении значения контекста, поэтому
<MyContext.Provider>
Чем больше пакетов и чем глубже слои, тем меньше будет производительность. -
<MyContext.Provider>
Когда значение пакета изменится, все компоненты будут перерисованы независимо от того, подписывается ли обернутый компонент на значение содержимого. -
В демке child2 не должен рендериться, как избежать лишнего рендеринга? *
Оптимизировано с помощью React.memo.
const Child2 = React.memo((props) => {
return <div>Child2</div>;
})
Результаты:
Уведомление:По умолчанию React.memo выполняет поверхностное сравнение только сложных объектов.Если вы хотите контролировать процесс сравнения, передайте пользовательскую функцию сравнения через второй параметр.Ссылка на ссылку
4. useRef
const refContainer = useRef(initialValue);
- useRef возвращает изменяемый объект ref, и единственная разница между созданием объекта {current: …} заключается в том, что useRef будет возвращать один и тот же объект ref каждый раз при его рендеринге, который уникален на протяжении всего жизненного цикла компонента.
- useRef может содержать любое изменяемое значение. Это похоже на то, как поля экземпляра используются в классе.Суммировать:
- useRef может хранить данные, которые не должны вызывать повторную визуализацию страницы..
- Если вы намеренно хотите прочитать состояние /latest/ из какого-либо асинхронного обратного вызова, вы можете использоватьреферичтобы сохранить его, изменить его и прочитать из него.
5. useReducer
const [state, dispatch] = useReducer(reducer, initialState);
reducer
это то, что может пройти толькоaction
Будуstate
от одного процесса к другомучистая функция;
useReducer
это способ прохождения(state,action) => newState
процесс, иredux
Работает так же. Поток данных: диспетчеризация (действие) => редуктор обновляет состояние => возвращает обновленное состояние
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case ‘decrement’:
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
Официально рекомендуется, чтобы useReducer лучше подходил для следующих сценариев:
- Логика состояния сложна и содержит несколько подзначений, которые можно обрабатывать централизованно.
- Следующее состояние зависит от предыдущего состояния.
- Хотите более стабильно создавать автоматизированные тест-кейсы.
- Если вы хотите глубоко изменить состояние некоторых подкомпонентов, использование useReducer также может оптимизировать производительность компонентов, которые будут запускать глубокие обновления, потому чтоВы можете передать отправку дочерним компонентам вместо обратных вызовов.
- Использование редьюсера помогает отделить чтение от записи.DEMO6
const fetchReducer = (state, action) => {
switch (action.type) {
case “FETCH_INIT":
return {
...state,
loading: true,
error: false
};
case “FETCH_SUCCESS”:
return {
...state,
loading: false,
error: false,
data: action.payload
};
case "FETCH_FAIL":
return {
…state,
loading: false,
error: true
};
default:
throw new Error();
}
};
function Demo6() {
const [state, dispatch] = useReducer(fetchReducer, {
loading: false,
error: false,
msg: "",
data: {}
});
const getData = useCallback(async () => {
try {
dispatch({ type: "FETCH_INIT" });
const response = await fetch(
"https://www.mxnzp.com/api/lottery/common/latest?code=ssq"
);
const res = await response.json();
if (res.code) {
dispatch({ type: "FETCH_SUCCESS", payload: res.data });
} else {
dispatch({ type: “FETCH_FAIL”, payload: res.msg });
}
} catch (error) {
dispatch({ type: “FETCH_FAIL”, payload: error });
}
}, []);
useEffect(() => {
getData();
}, [getData]);
return (
<Loading loading={state.loading}>
<p>开奖号码: {state.data.openCode}</p>
</Loading>
);
}
demo6
useReducer обрабатывает ряд логик, которые могут быть реализованы с помощью useState, в том числеloading, error, msg, data
.
useContext и useReducer имитируют состояние управления избыточностью
import React, { useReducer, useContext } from “react”;
const ModalContext = React.createContext();
const visibleReducer = (state, action) => {
switch (action.type) {
case “CREATE”:
return { ...state, ...action.payload };
case "EDIT":
return { ...state, ...action.payload };
default:
return state;
}
};
function Demo7() {
const initModalVisible = {
create: false,
edit: false
};
const [state, dispatch] = useReducer(visibleReducer, initModalVisible);
return (
<ModalContext.Provider value={{ visibles: state, dispatch }}>
<Demo7Child />
</ModalContext.Provider>
);
}
function Demo7Child() {
return (
<div>
Demo7Child
<Detail />
</div>
);
}
function Detail() {
const { visibles, dispatch } = useContext(ModalContext);
console.log("contextValue", visibles);
return (
<div>
<p>create: {`${visibles.create}`}</p>
<button
onClick={() => dispatch({ type: "CREATE", payload: { create: true } })}
>
打开创建modal
</button>
</div>
);
}
export default Demo7;
Логика явно извлечена, и значение в значении контекста не нужно прозрачно передавать в компоненте.из коробки.DEMO7
УведомлениеReact гарантирует, что идентификатор функции диспетчеризации стабилен и не изменится при повторном рендеринге компонента. Вот почему dispatch можно безопасно исключить из списка зависимостей useEffect или useCallback.
6. useCallback
грамматика:
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
вернутьmemoizedПерезвоните.
useCallback
В чем проблема? Первый взглядDEMO8
import React, { useRef, useEffect, useState, useCallback } from “react”;
function Child({ event, data }) {
console.log("child-render");
// 第五版
useEffect(() => {
console.log(“child-useEffect”);
event();
}, [event]);
return (
<div>
<p>child</p>
{/* <p>props-data: {data.data && data.data.openCode}</p> */}
<button onClick={event}>调用父级event</button>
</div>
);
}
const set = new Set();
function Demo8() {
const [count, setCount] = useState(0);
const [data, setData] = useState({});
// 第一版
// const handle = async () => {
// const response = await fetch(
// "https://www.mxnzp.com/api/lottery/common/latest?code=ssq"
// );
// const res = await response.json();
// console.log("handle", data);
// setData(res);
// };
// 第二版
// const handle = useCallback(async () => {
// const response = await fetch(
// “https://www.mxnzp.com/api/lottery/common/latest?code=ssq"
// );
// const res = await response.json();
// console.log(“handle”, data);
// setData(res);
// });
// 第三版
// const handle = useCallback(async () => {
// const response = await fetch(
// “https://www.mxnzp.com/api/lottery/common/latest?code=ssq”
// );
// const res = await response.json();
// setData(res);
// console.log(“useCallback”, data);
// // eslint-disable-next-line react-hooks/exhaustive-deps
// }, []);
// // 第四版
// const handle = useCallback(async () => {
// const response = await fetch(
// “https://www.mxnzp.com/api/lottery/common/latest?code=ssq"
// );
// const res = await response.json();
// setData(res);
// console.log(“parent-useCallback", data);
// // eslint-disable-next-line react-hooks/exhaustive-deps
// }, []);
// 第五版
const handle = useCallback(async () => {
const response = await fetch(
"https://www.mxnzp.com/api/lottery/common/latest?code=ssq"
);
const res = await response.json();
setData(res);
console.log("parent-useCallback", data);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [count]);
set.add(handle);
console.log(“parent-render====>”, data);
return (
<div>
<button
onClick={e => {
setCount(count + 1);
}}
>
count++
</button>
<p>set size: {set.size}</p>
<p>count:{count}</p>
<p>data: {data.data && data.data.openCode}</p>
<p>-------------------------------</p>
<Child event={handle} />
</div>
);
}
export default Demo8;
В заключение:
- Первая версия: каждый раз, когда вы рендерите, handle — это новая функция, и вы можете каждый раз получать самые свежие данные.
- Второй вариант: обернуть хендл с помощью useCallback, каждый раз при рендеринге хендл тоже новая функция, и можно каждый раз получать самые свежие данные, эффект такой же, как и в первом варианте, поэтому использовать его не рекомендуется.
- Третий вариант: useCallback Если второй параметр deps, то дескриптор будет мемоизирован, поэтому каждое данные является данными (замыканием) первой памяти.
- Четвертая редакция: useCallback зависит от изменения счетчика, всякий раз, когда useCallback изменяется, дескриптор будет повторно запоминаться.
- Пятая редакция: при каждом изменении счетчика функция, передаваемая дочернему компоненту, является самой последней, поэтому выполняется useEffect дочернего элемента.Суммировать:
- useCallback вернетВерсия обратного вызова памяти, только если одна из зависимостей изменилась.
- Этот метод полезен при передаче обратных вызовов оптимизированным дочерним компонентам, которые полагаются на равенство ссылок, чтобы предотвратить ненужную визуализацию.
- Используйте функцию обратного вызова в качестве параметра для передачи, каждый раз, когда функция рендеринга будет меняться, это также вызовет повторную визуализацию дочернего компонента, useCallback может оптимизировать повторную визуализацию.Вопрос: Как оптимизировать ненужный рендеринг дочерних компонентов?
7. useMemo
грамматика:
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
; вернутьmemoizedзначение иuseCallback
Точно так же мемоизированное значение пересчитывается только при изменении зависимостей.
Разница между useMemo и useCallback в том, что они позволяют вамmemoizedПрименяется к любому типу значения (не только к функциям).DEMO9
import React, { useState, useMemo } from “react”;
function Demo9() {
const [count, setCount] = useState(0);
const handle = () => {
console.log(“handle”, count);
return count;
};
const handle1 = useMemo(() => {
console.log("handle1", count);
return count;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const handle2 = useMemo(() => {
console.log(“handle2”, count);
// 大计算量的方法
return count;
}, [count]);
console.log("render-parent");
return (
<div>
<p>
demo9: {count}
<button onClick={() => setCount(count + 1)}>++count</button>
</p>
<p>-------------------</p>
<Child handle={handle1} />
</div>
);
}
function Child({ handle }) {
console.log("render-child");
return (
<div>
<p>child</p>
<p>props-data: {handle}</p>
</div>
);
}
export default Demo9;
Суммировать:
-
useMemo
Будет вrender
перед казнью. - Если массив зависимостей не предоставлен, useMemo будет вычислять новые значения при каждом рендеринге.
-
useMemo
для возвратаmemoize
, чтобы предотвратить каждый рендербольшой объем вычисленийпонесенные рассходы. - использовать
useMemo
Оптимизация должна быть осторожной, потому что сама по себе оптимизация также требует вычислений,В большинстве случаев вам не нужно думать об оптимизации ненужных повторных рендеров..
Другие крючки
1. useImperativeHandle
// ref:需要传递的ref
// createHandle: 需要暴露给父级的方法。
// deps: 依赖
useImperativeHandle(ref, createHandle, [deps])
useImperativeHandle
должно быть сforwardRef
использовать вместе. Первый взглядDEMO10
import React, {
useRef,
forwardRef,
useImperativeHandle,
useEffect,
useState
} from "react";
const Child = forwardRef((props, ref) => {
const inputEl = useRef();
const [value, setVal] = useState("");
// 第一版
// useImperativeHandle(ref, () => {
// console.log("useImperativeHandle");
// return {
// value,
// focus: () => inputEl.current.focus()
// };
// });
// 第二版
useImperativeHandle(
ref,
() => {
console.log(“useImperativeHandle");
return {
value,
focus: () => inputEl.current.focus()
};
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
);
return (
<input
ref={inputEl}
onChange={e => setVal(e.target.value)}
value={value}
{...props}
/>
);
});
function Demo10() {
const inputEl = useRef(null);
useEffect(() => {
console.log(“parent-useEffect”, inputEl.current);
inputEl.current.focus();
}, []);
function click() {
console.log("click:", inputEl.current);
inputEl.current.focus();
}
console.log(“Demo10”, inputEl.current);
return (
<div>
<Child ref={inputEl} />
<button onClick={click}>click focus</button>
</div>
);
}
export default Demo10;
В заключение:
-
useImperativeHandle
Выполняется после рендеринга текущего компонента. - Первая версия: никаких зависимостей, всякий раз, когда ререндерится,
useImperativeHandle
будет выполнено, и можно будет получить самое последнее значение в состоянии, и метод, переданный родительским компонентом, также будет самым последним. - Второе издание: зависимость
[]
, при каждом повторном рендерингеuseImperativeHandle
Не выполняется и не обновляется до родительского компонента. - 3-е издание: зависит от входящего значения состояния
[value]
, для достижения желаемого эффекта.
2. useDebugValue
Обычно не используется, его можно увидеть только в инструментах разработчика React, см.портал.
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(false);
// 在开发者工具中的这个 Hook 旁边显示标签
// e.g. "FriendStatus: Online"
useDebugValue(isOnline ? "Online" : "Offline");
return isOnline;
}
function Demo11() {
const isOnline = useFriendStatus(567);
return <div>朋友是否在线:{isOnline ? "在线" : "离线"}</div>;
}
3. useLayoutEffect
используется редко, сuseEffect
То же самое, но эффект будет вызываться синхронно после всех изменений DOM, см. официальныйпортал.
📢 Наконец-то
Добро пожаловать на общение,😀🍻🍻😀
следующее уведомление
«Практика кастомных хуков в проектах»
Связанное чтение:
- When to useMemo and useCallback
- thinking-in-react
- React hooks: not magic, just arrays - Rudi Yardley - Medium
- ву ву ву.net show.com/blog/2019/0…
- Глубокое погружение: как работают хуки React? - Знаю почти
Теплое напоминание: ** Больше растягивайтесь, это полезно для вашего тела.
【Серия React Hook】
- Серия React Hook (2): некоторые приемы настройки хуков
- Серия React Hook (3): вспомните осадки Hook в проекте на Среднем Тайване
Гость офицер, качество три подряд! 🤡🤡🤡