Factor out input to isolate component updates
This commit is contained in:
parent
5048c460f1
commit
652d98f6c6
2 changed files with 39 additions and 14 deletions
36
ui/component/common/debounced-input.jsx
Normal file
36
ui/component/common/debounced-input.jsx
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
// @flow
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import Icon from 'component/common/icon';
|
||||||
|
import useDebounce from 'effects/use-debounce';
|
||||||
|
|
||||||
|
const FILTER_DEBOUNCE_MS = 300;
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
icon?: string;
|
||||||
|
value?: string;
|
||||||
|
placeholder?: string;
|
||||||
|
onChange: (newValue: string) => any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function DebouncedInput(props: Props) {
|
||||||
|
const { icon, value = '', placeholder = '', onChange } = props;
|
||||||
|
const [rawValue, setRawValue] = useState(value);
|
||||||
|
const debouncedValue: string = useDebounce(rawValue, FILTER_DEBOUNCE_MS);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onChange(debouncedValue);
|
||||||
|
}, [onChange, debouncedValue]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="wunderbar">
|
||||||
|
{icon && <Icon icon={icon} />}
|
||||||
|
<input
|
||||||
|
className="wunderbar__input"
|
||||||
|
spellCheck={false}
|
||||||
|
placeholder={placeholder}
|
||||||
|
value={rawValue}
|
||||||
|
onChange={(e) => setRawValue(e.target.value.trim())}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -8,15 +8,14 @@ import Button from 'component/button';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import Icon from 'component/common/icon';
|
import Icon from 'component/common/icon';
|
||||||
import NotificationBubble from 'component/notificationBubble';
|
import NotificationBubble from 'component/notificationBubble';
|
||||||
|
import DebouncedInput from 'component/common/debounced-input';
|
||||||
import I18nMessage from 'component/i18nMessage';
|
import I18nMessage from 'component/i18nMessage';
|
||||||
import ChannelThumbnail from 'component/channelThumbnail';
|
import ChannelThumbnail from 'component/channelThumbnail';
|
||||||
import useDebounce from 'effects/use-debounce';
|
|
||||||
import { useIsMobile, useIsLargeScreen } from 'effects/use-screensize';
|
import { useIsMobile, useIsLargeScreen } from 'effects/use-screensize';
|
||||||
import { GetLinksData } from 'util/buildHomepage';
|
import { GetLinksData } from 'util/buildHomepage';
|
||||||
import { DOMAIN, ENABLE_UI_NOTIFICATIONS, ENABLE_NO_SOURCE_CLAIMS, CHANNEL_STAKED_LEVEL_LIVESTREAM } from 'config';
|
import { DOMAIN, ENABLE_UI_NOTIFICATIONS, ENABLE_NO_SOURCE_CLAIMS, CHANNEL_STAKED_LEVEL_LIVESTREAM } from 'config';
|
||||||
|
|
||||||
const FOLLOWED_ITEM_INITIAL_LIMIT = 10;
|
const FOLLOWED_ITEM_INITIAL_LIMIT = 10;
|
||||||
const FILTER_DEBOUNCE_MS = 300;
|
|
||||||
|
|
||||||
type SideNavLink = {
|
type SideNavLink = {
|
||||||
title: string,
|
title: string,
|
||||||
|
@ -252,8 +251,7 @@ function SideNavigation(props: Props) {
|
||||||
const showSubscriptionSection = shouldRenderLargeMenu && isPersonalized && subscriptions && subscriptions.length > 0;
|
const showSubscriptionSection = shouldRenderLargeMenu && isPersonalized && subscriptions && subscriptions.length > 0;
|
||||||
const showTagSection = sidebarOpen && isPersonalized && followedTags && followedTags.length;
|
const showTagSection = sidebarOpen && isPersonalized && followedTags && followedTags.length;
|
||||||
|
|
||||||
const [rawSubscriptionFilter, setRawSubscriptionFilter] = React.useState('');
|
const [subscriptionFilter, setSubscriptionFilter] = React.useState('');
|
||||||
const subscriptionFilter: string = useDebounce(rawSubscriptionFilter, FILTER_DEBOUNCE_MS);
|
|
||||||
|
|
||||||
const filteredSubscriptions = subscriptions.filter(
|
const filteredSubscriptions = subscriptions.filter(
|
||||||
(sub) => !subscriptionFilter || sub.channelName.toLowerCase().includes(subscriptionFilter)
|
(sub) => !subscriptionFilter || sub.channelName.toLowerCase().includes(subscriptionFilter)
|
||||||
|
@ -307,16 +305,7 @@ function SideNavigation(props: Props) {
|
||||||
<>
|
<>
|
||||||
<ul className="navigation__secondary navigation-links">
|
<ul className="navigation__secondary navigation-links">
|
||||||
<li className="navigation-item">
|
<li className="navigation-item">
|
||||||
<div className="wunderbar">
|
<DebouncedInput icon={ICONS.SEARCH} placeholder={__('Filter')} onChange={setSubscriptionFilter} />
|
||||||
<Icon icon={ICONS.SEARCH} />
|
|
||||||
<input
|
|
||||||
className="wunderbar__input"
|
|
||||||
spellCheck={false}
|
|
||||||
placeholder={__('Filter')}
|
|
||||||
value={rawSubscriptionFilter}
|
|
||||||
onChange={(e) => setRawSubscriptionFilter(e.target.value.trim())}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</li>
|
</li>
|
||||||
{displayedSubscriptions.map((subscription) => (
|
{displayedSubscriptions.map((subscription) => (
|
||||||
<SubscriptionListItem key={subscription.uri} subscription={subscription} />
|
<SubscriptionListItem key={subscription.uri} subscription={subscription} />
|
||||||
|
|
Loading…
Reference in a new issue