这是我参与「第五届青训营 」伴学笔记创作活动的第 7天
我们组选择用react+typescript来构建组件库,我今天先看了下别人的select组件
select组件是外部容器组件,本select组件使用复合组件模式。
Select 组件是维护状态的容器组件。构建选择组件需要两个主要状态。
- selectedOption(当前选择的选项的值)。
- showDropdown(显示或隐藏下拉列表的布尔值)。
import React, { ReactNode, useState, useRef } from "react";
import useOnClickOutside from "../../hooks/useOnClickOutside";
const Select: React.FC<{
children: ReactNode | ReactNode[];
defaultValue?: string;
placeholder?: string;
}> = ({ children, defaultValue, placeholder }) => {
const [selectedOption, setSelectedOption] = useState(defaultValue || "");
const [showDropdown, setShowDropdown] = useState(false);
const showDropdownHandler = () => setShowDropdown(!showDropdown);
const selectPlaceholder = placeholder || "Choose an option";
const clickOutsideHandler = () => setShowDropdown(false);
// custom hook to detect the click on the outside
useOnClickOutside(selectContainerRef, clickOutsideHandler);
const updateSelectedOption = (option: string) => {
setSelectedOption(option);
setShowDropdown(false);
};
return (
<div className="select-container" ref={selectContainerRef}>
<div
className={showDropdown ? "selected-text active" : "selected-text"}
onClick={showDropdownHandler}
>
{selectedOption.length > 0 ? selectedOption : selectPlaceholder}
</div>
<ul
className={
showDropdown
? "select-options show-dropdown-options"
: "select-options hide-dropdown-options"
}
>
{children}
</ul>
</div>
);
};
export default Select;
我正在使用一个名为useOnClickOutside的自定义挂钩。这个钩子就像一个接受 ref 和回调函数的监听器。只要在指定 ref 之外发生点击事件,就会调用回调函数。每当用户在选择组件外部单击时,我在这里使用这个自定义挂钩来隐藏下拉列表。现在我们已经完成构建外部组件(选择)。下一步是构建选项组件。
import React, { ReactNode } from "react";
const Option: React.FC<{
children: ReactNode | ReactNode[];
value: string;
}> = ({ children, value }) => {
return (
<li className="select-option">
{children}
</li>
);
};
export default Option;
选项组件现已构建。现在是困难的部分,我们需要使用 Option 组件中的 select 组件中的 updateSelectedOption 函数。我们如何在不通过 props 的情况下共享 Select 和 Option 组件之间的功能或状态? 冷静点,React 提供了一种无需通过 props 即可共享数据的方法,这就是 React 上下文发挥作用的地方。如果您不熟悉 React 上下文,请参阅reactjs.org/docs/contex…。
Context 提供了一种通过组件树传递数据的方法,而无需在每个级别手动传递 props。
现在让我们为选择组件编写上下文。我们将共享两个值:selectedOption 和 updateSelectedOption。
import { createContext, useContext } from "react";
const SelectContext = createContext<{
selectedOption: string;
changeSelectedOption: (option: string) => void;
}>({
selectedOption: "",
changeSelectedOption: (option: string) => {}
});
const useSelectContext = () => {
const context = useContext(SelectContext);
if (!context) {
throw new Error("Error in creating the context");
}
return context;
};
export { useSelectContext, SelectContext };
我们已经创建了选择上下文,useSelectContext 是使用上下文的自定义挂钩。现在我们需要为上下文提供值。我们可以使用 SelectContext.Provider 元素为上下文提供值。
// Select component
<SelectContext.Provider
value={{ selectedOption, changeSelectedOption: updateSelectedOption }}
>
<div className="select-container" ref={selectContainerRef}>
...
...
</div>
</SelectContext.Provider>
现在我们已经为 Context 提供了值。下一步是使用 Option 组件中提供的值。我们可以使用 useSelectContext 从上下文中获取值。
import React, { ReactNode } from "react";
import { useSelectContext } from "./selectContext";
const Option: React.FC<{
children: ReactNode | ReactNode[];
value: string;
}> = ({ children, value }) => {
const { changeSelectedOption } = useSelectContext();
return (
<li className="select-option" onClick={() => changeSelectedOption(value)}>
{children}
</li>
);
};
export default Option;
我们现在已经连接了 Option & Select 组件。单击任何选项将触发 changeSelectedOption 函数,该函数将更新 selectedOption 状态。我们还可以在选项组件的上下文中使用 selectedOption 值来突出显示所选选项。
<Select>
<Option value="one">One</Option>
<Option value="two">Two</Option>
<Option value="three">Three</Option>
<Option value="four">Four</Option>
</Select>