На React Conf в прошлом месяце основная команда React впервые представила публике зацепки. Сначала я увидел такую странную вещь и очень сопротивлялся этому. Дэн сказал, что это в JavaScript слишком мрачно, и людям, пришедшим с других языков писать React, будет очень неудобно. Однако хуки — это тоже некая черная магия по своей сути, нужно понимать ее суть и еще нужно очень досконально разбираться в различных замыканиях и скоупах JS.
Однако после нескольких дней работы с хуками идея показалась мне довольно интересной. Во-первых, я рекомендую вступительную речь на React Conf:React Today and Tomorrow and 90% Cleaner React With Hooks, стоит посмотреть.
Наша команда всегда любила RxJS, но его использование в сочетании с React всегда было немного громоздким. на прошлой неделе
@ тоже волкРешил проверить воду на крючках апи. В результате весь день я слышал, как люди вокруг меня кричали: «Это действительно ароматно».rxjs-hooks
Итак, насколько вкусно писать код RxJS с хуками? Давайте взглянем на этот проект с открытым исходным кодом, который радовал мою маму и открывался снова и снова:LeetCode-OpenSource/rxjs-hooks
rxjs-hooks имеет полные тестовые примеры со 100% покрытием тестами. На данный момент есть только два крючка:useObservable
а такжеuseEventCallback
. Или объясните прямо на примере, давайте сначала вспомним, как создать, подписаться и уничтожить поток в React Component. Это выглядит так:
import React from 'react';
import { interval } from 'rxjs';
import { tap } from 'rxjs/operators';
class Timer extends React.Component {
state = {
val: 0,
};
subscription = new Subscription();
componentDidMount() {
const sub = interval(1000).pipe(
tap((val) => this.setState({ val }))
)
this.subscription.add(sub)
}
componentWillUnmount() {
this.subscription.unsubscribe()
}
render() {
return <h1>{this.state.val}</h1>
}
}
Подписка вручную, управление циклами объявлений вручную и создание моста к функции рендеринга (UI) через состояние в React. Затем после использования rxjs-хуков:
import React from 'react';
import { interval } from 'rxjs';
import { useObservable } from 'rxjs-hooks';
function Timer() {
const val = useObservable(() => interval(1000), 0);
return <h1>{val}</h1>
}
Никаких ручных подписок, не нужно беспокоиться об управлении жизненным циклом. просто нужен одинМенее 1 КБ зависимостей, вы можете с радостью использовать RxJS в мире React.
Подробный API
В этом разделе кратко представлены два API в rxjs-hooks с некоторыми примерами. Подробные определения типов могут бытьпосетите здесьПроверить. Нижеследующее будет объяснено на примере, поэтому его будет легче понять.
Уведомление
- Следующие случаи основаны на RxJS 6
- Если вы недостаточно знаете о хуках React, рекомендуется посмотреть видео, рекомендованное в начале статьи, илиОфициальный блог React.
1. useObservable
Случай 1: нет значения по умолчанию, нет зависимости от внешнего состояния
function Timer() {
const val = useObservable(() => interval(1000));
return <h1>{val}</h1>
}
В этом случае передается только первый параметр, который является фабричной функцией Observable, которая должна возвращать Observable, а возвращаемое значение useObservable всегда является последним значением потока. Только один контент пуст при первом рендеринге<h1>
; через 1 секунду содержимое становится0
; через 2 секунды содержимое становится1
…
Случай 2: есть значения по умолчанию
function Timer() {
const val = useObservable(() => interval(1000), -1);
return <h1>{val}</h1>
}
Во втором случае мы передаем второй параметр, которыйval
значение по умолчанию для . Таким образом, в этом случае первое отображаемое содержимое уже не пустое, а-1
.
Случай 3: зависит от последнего состояния выполнения
Если вам нужно получить результат последнего вывода в потоке, фабричная функция перейдет вstate$
Поток, чтобы помочь вам сделать именно это. (необходимо использовать здесьwithLatestFrom
объединить этот поток, иначе это вызовет бесконечный цикл)
function Timer() {
const val = useObservable((state$) => interval(1000).pipe(
withLatestFrom(state$),
map(([index, prevVal]) => index + prevVal),
), 0);
// first render: 0
// 1s later: 1 (1 + 0)
// 2s later: 3 (2 + 1)
// 3s later: 6 (3 + 3)
// 4s later: 10 (4 + 6)
return <h1>{val}</h1>
}
Случай 4: Зависимость от внешнего состояния
Фабричная функция может полагаться на некоторое внешнее состояние, переданное через третий параметр useObservable (иuseEffect,useMemoИнтерфейс похож)
Если передать третий параметр, то в фабричной функции вы получите два потока, которыеinput$
а такжеstate$
. В приведенном ниже примереinput$
Значение, испускаемое потоком, всегда[a, b]
кортеж. Чтобы упростить понимание примера, мы не будем использоватьstate$
поток.
function Timer({ a }) {
const [b, setB] = useState(0);
const val = useObservable(
(inputs$, _state$) => timer(1000).pipe(
combineLatest(inputs$),
map(([_, [a, b]]) => a + b),
),
0,
[a, b],
);
return (
<div>
<h1>{val}</h1>
<button onClick={() => setB(b + 10)}>b: +10</button>
</div>
)
}
function App() {
const [a, setA] = useState(100);
return (
<div>
<Timer a={a} />
<button onClick={() => setA(a + 100)}>a: +100</button>
</div>
);
}
Этот пример относительно сложен и может быть объединен сlive demoпонимать.
2. useEventCallback
Мы считаем, что RxJS не только очень хорошо справляется с потоком данных, но и очень полезен при обработке некоторой логики взаимодействия. Поэтому мы разработали второй APIuseEventCallback
, который принимает три параметра. где последние два параметра совпадают сuseObservable
Есть много общего, поэтому здесь мы сосредоточимся на первой форме, связанной с возвращаемым значением.
Сначала посмотрите на следующий пример (live demo), легко увидеть, что возвращаемое значение не совпадает с useEventCallback, оно вернет[callback, value]
кортеж. Также принимает фабричную функцию, которая принимаетevent$
параметр, всякий разcallback
когда звонили,event$
Поток всегда имеет новое значение, вытекающее наружу. а такжеuseEventCallback
Второй параметр функции по-прежнему является привычным значением по умолчанию.
function App() {
const [clickCallback, [description, x, y]] = useEventCallback((event$) =>
event$.pipe(
map((event) => [event.target.innerHTML, event.clientX, event.clientY]),
),
["nothing", 0, 0],
)
return (
<div className="App">
<h1>click position: {x}, {y}</h1>
<h1>"{description}" was clicked.</h1>
<button onClick={clickCallback}>click me</button>
<button onClick={clickCallback}>click you</button>
<button onClick={clickCallback}>click him</button>
</div>
);
}
Больше практических случаев
Вот несколько простых практических примеров, которые помогут вам лучше понять использование rxjs-hooks. Код не будет опубликован в тексте. Заинтересованные друзья могут перейти по онлайн-ссылке в случае ниже, чтобы играть.
Случай 1: Перетащите меня
Пример: двухколоночный макет с изменяемым размером
Случай: скользящая очередь
резюме
слишком далекоrxjs-hooksПросто представь это здесь. Наша реализация не обязательно является лучшим пониманием хуков, и она должна быть хорошей идеей. Мы надеемся, что больше людей в сообществе примут участие в этом изменении, и мы рады поделиться с вами различными путешествиями, с которыми мы столкнулись. В то же время вы можете в любое время отправлять вопросы или PR в этот проект.
В настоящее время у команды LeetCode есть как проекты, разработанные в сотрудничестве с американскими командами, так и независимые продуктовые линейки в Китае. Размер команды относительно небольшой и сложный, и мы всегда рады присоединиться к лучшим и потенциальным членам.
Вакансии и уровень заработной платы:
- Бэкенд-инженер Python 20-40 тыс.
- Младший Front-end Engineer 18к-25к
- Старший Front-end инженер 25к-45к
- Full stack инженер 25к-45к
Отправьте свое резюме:Присоединяйтесь к нам - LeetCodeили напишите мне напрямуюjerry@leetcode.com