Created
November 14, 2024 08:59
-
-
Save lucassshanks/078d458b189dabbc2da8fb22167dc1e1 to your computer and use it in GitHub Desktop.
Use react-window to limit the amount of items rendered in the dom in TS. The onHover will lag due to how react-select computes whether an element should be highlighted or not so a CSS solution is needed to solve this. Also included some example of data fetching. Designed for https://github.com/JedWatson/react-select/issues/3128
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useEffect, useState } from 'react' | |
import ReactSelect, { createFilter, MenuListProps, OptionProps } from 'react-select' | |
import { FixedSizeList as List, ListChildComponentProps } from 'react-window' | |
type OptionType = { | |
label: string | |
value: string | |
} | |
export const ReactWindowSelectExample = () => { | |
return ( | |
<ReactSelect | |
onChange={handleOnChange} | |
options={Options} | |
components={{ MenuList, Option }} | |
filterOption={createFilter({ ignoreAccents: false })} // To improve performance on filtering | |
/> | |
) | |
} | |
const MenuList = (props: MenuListProps<OptionType, false>) => { | |
const itemHeight = 35 | |
const { options, children, maxHeight, getValue } = props | |
const [value] = getValue() | |
const initialOffset = options.indexOf(value) * itemHeight | |
return Array.isArray(children) ? ( | |
<div style={{ paddingTop: 4 }}> | |
<List | |
height={maxHeight} | |
itemCount={children.length} | |
itemSize={itemHeight} | |
initialScrollOffset={initialOffset} | |
width="100%" | |
> | |
{({ index, style }: ListChildComponentProps) => <div style={{ ...style }}>{children[index]}</div>} | |
</List> | |
</div> | |
) : null | |
} | |
const Option = (props: OptionProps<OptionType>) => { | |
const { children, innerProps, getStyles, isSelected } = props | |
delete props.innerProps.onMouseMove | |
delete props.innerProps.onMouseOver | |
const [isHovered, setIsHovered] = useState(false) | |
const [isPressed, setIsPressed] = useState(false) | |
// Emulate default react-select styles | |
const customStyles = { | |
...getStyles('option', props), | |
...(isSelected || isHovered || isPressed | |
? { | |
backgroundColor: isSelected ? '#abbde9' : isPressed ? '#B2D4FF' : '#DDEBFF', | |
} | |
: {}), | |
color: isSelected ? 'white' : undefined, | |
} | |
return ( | |
<div | |
style={customStyles as React.CSSProperties} | |
role="button" | |
id={innerProps.id} | |
tabIndex={innerProps.tabIndex} | |
onMouseEnter={() => setIsHovered(true)} | |
onMouseLeave={() => setIsHovered(false)} | |
onMouseDown={() => setIsPressed(true)} | |
onMouseUp={() => setIsPressed(false)} | |
onClick={innerProps.onClick} | |
onKeyDown={innerProps.onKeyDown} | |
> | |
{children} | |
</div> | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment