Рукописные хуки React
- Хуки — это новая функция в React 16.8, которая позволяет вам использовать состояние и другие функции React без написания классов.
- Все API React, начиная с использования, являются хуками.
что такое крючок
Хук — это специальная функция, которая позволяет вам «подключаться» к функциям React. Например, useState — это хук, который позволяет вам добавлять состояние к компонентам функции React.
Зачем использовать хуки
Цитирую описание официального сайта
-
Повторное использование логики состояния между компонентами сложно
Вы можете использовать свойства рендеринга (свойства рендеринга) или HOC (компоненты более высокого порядка), но независимо от того, являются ли это свойствами рендеринга или компонентами более высокого порядка, родительский контейнер (обычно элемент div) будет обернут вокруг исходного компонента. в приложениях React в React DevTools вы обнаружите, что компоненты, состоящие из поставщиков, потребителей, компонентов более высокого порядка, реквизитов рендеринга и других слоев абстракции, образуют «вложенный ад». -
Сложные компоненты становятся трудными для понимания
Компоненты часто извлекают данные в componentDidMount и componentDidUpdate. Однако тот же componentDidMount может содержать и много другой логики, например, настройку прослушивателей событий, которые нужно очищать позже в componentWillUnmount. Код, который связан друг с другом и нуждается в изменении, разделяется, а код, который совершенно не связан, объединяется в одном методе. так легко создавать ошибки -
непонятный класс
это указывает на проблему: когда родительский компонент передает функцию дочернему компоненту, он должен связать это
Правила хука
- Хук можно вызывать только в самом внешнем слое внутри функции, а не в циклах, условных суждениях или подфункциях.
- Вызывайте хуки только в функциях React Вызов хуков в функциональных компонентах React Вызов других хуков в пользовательских хуках
Используйте eslint для проверки правил хуков
Используйте eslint-plugin-react-hooks для проверки ошибок кода
{
"plugins": ["react-hooks"],
// ...
"rules": {
"react-hooks/rules-of-hooks": 'error',// 检查 Hook 的规则
"react-hooks/exhaustive-deps": 'warn' // 检查 effect 的依赖
}
}
useState
useState возвращает массив: состояние и функцию для обновления состояния.
Аналогично this.setState компонентов класса, но он не будет объединять новое состояние со старым состоянием, а заменит его напрямую.
// 保存状态的数组
let hookStates = [];
// 索引
let hookIndex = 0;
function useState(initialState) {
hookStates[hookIndex] = hookStates[hookIndex] || initialState;
// 利用闭包维护函数调用位置
let currentIndex = hookIndex;
function setState(newState) {
// 判断传入的state是否为函数,如果是把prevState传入
if (typeof newState === "function") {
// 重新复制给newState
newState = newState(hookStates[hookIndex]);
}
// 更新state
hookStates[currentIndex] = newState;
// 触发视图更新
render();
}
// 返回数组形式,解构可写成任意变量
return [hookStates[hookIndex++], setState];
}
useEffect
useEffect — это эффект-хук, который добавляет возможность манипулировать побочными эффектами в функциональные компоненты. Он имеет то же назначение, что и componentDidMount, componentDidUpdate и componentWillUnmount в компоненте класса, но объединен в один API.
В отличие от componentDidMount или componentDidUpdate, эффекты, отправленные с помощью useEffect, не блокируют обновление представления браузером, что делает ваше приложение более отзывчивым. В особых случаях (например, при измерении макетов) есть отдельный хук useLayoutEffect, который используется так же, как и useEffect.
//保存状态的数组
let hookStates = [];
//索引
let hookIndex = 0;
function useEffect(callback, dependencies) {
if (hookStates[hookIndex]) {
// 非初始调用
let lastDependencies = hookStates[hookIndex];
// 判断传入依赖项跟上一次是否相同
let same = dependencies.every(
(item, index) => item === lastDependencies[index]
);
if (same) {
hookIndex++;
} else {
hookStates[hookIndex++] = dependencies;
callback();
}
} else {
// 初始调用
hookStates[hookIndex++] = dependencies;
callback();
}
}
useMemo
Позволяет кэшировать результаты вычислений между рендерами, «запоминая» последний результат вычислений.
Упрощает контроль при обновлении конкретных дочерних узлов, уменьшая потребность в чистых компонентах.
// 保存状态的数组
let hookStates = [];
// 索引
let hookIndex = 0;
function useMemo(factory, dependencies) {
if (hookStates[hookIndex]) {
// 非首次
let [lastMemo, lastDependencies] = hookStates[hookIndex];
// 判断传入依赖项跟上一次是否相同
let same = dependencies.every(
(item, index) => item === lastDependencies[index]
);
if (same) {
hookIndex++;
return lastMemo;
} else {
// 只要有一个依赖变量不一样的话
let newMemo = factory();
hookStates[hookIndex++] = [newMemo, dependencies];
return newMemo;
}
} else {
// 首次调用
let newMemo = factory();
hookStates[hookIndex++] = [newMemo, dependencies];
return newMemo;
}
}
useCallback
Позволяет вам сохранять ссылку на один и тот же обратный вызов между повторными рендерингами, чтобы поддерживать работу shouldComponentUpdate.
// 保存状态的数组
let hookStates = [];
// 索引
let hookIndex = 0;
function useCallback(callback, dependencies) {
if (hookStates[hookIndex]) {
// 非首次
let [lastCallback, lastDependencies] = hookStates[hookIndex];
let same = dependencies.every(
(item, index) => item === lastDependencies[index]
);
if (same) {
hookIndex++;
return lastCallback;
} else {
// 只要有一个依赖变量不一样的话
hookStates[hookIndex++] = [callback, dependencies];
return callback;
}
} else {
// 首次调用
hookStates[hookIndex++] = [callback, dependencies];
return callback;
}
}
memo
function memo(OldFunctionComponent) {
return class extends React.PureComponent {
render() {
return <OldFunctionComponent {...this.props} />;
}
};
}
useContext
Получает объект контекста (возвращаемое значение React.createContext) и возвращает текущее значение контекста.
useContext(MyContext) просто позволяет вам читать значение контекста и подписываться на изменения в контексте. По-прежнему необходимо использовать
function useContext(context) {
return context._currentValue;
}
// 父组件
const CountCtx = React.createContext();
function ParentComp() {
const [state, setState] = React.useState({ number: 0 });
return (
<CountCtx.Provider value={{ state, setState }}>
<Child />
</CountCtx.Provider>
);
}
// 子组件
function Child() {
let { state, setState } = useContext(CountCtx);
return (
<div>
<p>{state.number}</p>
<button onClick={() => setState({ number: state.number + 1 })}>
add
</button>
</div>
);
}
useRef
useRef возвращает изменяемый объект ref, текущее свойство которого инициализируется переданным параметром. Объект ref, возвращаемый useRef, остается неизменным на протяжении всего жизненного цикла компонента, то есть каждый раз, когда функциональный компонент повторно визуализируется, возвращаемый объект ref остается одним и тем же (обратите внимание, что при использовании React.createRef он будет повторно -создается каждый раз при повторном рендеринге компонента ref)
let lastRef;
function useRef(value) {
lastRef = lastRef || { current: value };
return lastRef;
}
useReducer
useReducer очень похож на редуктор в редукции. useState реализуется внутри useReducer
// 保存状态的数组
let hookStates = [];
// 索引
let hookIndex = 0;
function useReducer(reducer, initialState) {
hookStates[hookIndex] = hookStates[hookIndex] || initialState;
let currentIndex = hookIndex;
function dispatch(action) {
hookStates[currentIndex] = reducer
? reducer(hookStates[currentIndex], action)
: action;
// 触发视图更新
render();
}
return [hookStates[hookIndex++], dispatch];
}
// useState可以使用useReducer改写
function useState(initialState) {
return useReducer(null, initialState);
}
Ссылаться на
React Hooks Подробно почти 1W [слово] + бой проекта
рекомендовать
Получить значение экземпляра дочернего компонента из родительского компонента React Hooks