javascript - 关于这个 React 自定义钩子用法的困惑
问题描述
我正在看一些关于 React Hooks 的教程,在教程中作者创建了一个useDropdown
用于呈现可重用下拉列表的钩子。代码是这样的
import React, { useState } from "react";
const useDropdown = (label, defaultState, options) => {
const [state, updateState] = useState(defaultState);
const id = `use-dropdown-${label.replace(" ", "").toLowerCase()}`;
const Dropdown = () => (
<label htmlFor={id}>
{label}
<select
id={id}
value={state}
onChange={e => updateState(e.target.value)}
onBlur={e => updateState(e.target.value)}
disabled={!options.length}
>
<option />
{options.map(item => (
<option key={item} value={item}>
{item}
</option>
))}
</select>
</label>
);
return [state, Dropdown, updateState];
};
export default useDropdown;
他在这样的组件中使用了它
import React, { useState, useEffect } from "react";
import useDropdown from "./useDropdown";
const SomeComponent = () => {
const [animal, AnimalDropdown] = useDropdown("Animal", "dog", ANIMALS);
const [breed, BreedDropdown, updateBreed] = useDropdown("Breed", "", breeds);
return (
<div className="search-params">
<form>
<label htmlFor="location">
Location
<input
id="location"
value={location}
placeholder="Location"
onChange={e => updateLocation(e.target.value)}
/>
</label>
<AnimalDropdown />
<BreedDropdown />
<button>Submit</button>
</form>
</div>
);
};
export default SomeComponent;
他说这样我们可以创建可重用的下拉组件。我想知道这与定义一个普通的旧 Dropdown 组件并将道具传递给它有什么不同。在这种情况下,我能想到的唯一区别是,现在我们能够在父组件(即)中获取状态和 setState,并直接从那里SomeComponent
读取/设置子组件(即由 输出的组件)的状态。useDropdown
然而,这是否被认为是一种反模式,因为我们正在打破单向数据流?
解决方案
虽然对于如何定义自定义钩子以及应该包含什么逻辑没有硬核限制,但它是编写返回 JSX 的钩子的反模式
您应该评估每种方法给您带来的好处,然后决定一段特定的代码
使用钩子返回 JSX 有一些缺点
- 当您编写一个返回 JSX 组件的钩子时,您实际上是在功能组件中定义该组件,因此在每次重新渲染时,您都将创建该组件的一个新实例。这将导致组件被卸载并再次安装。如果您在组件中有状态登录,这对性能不利并且还会出现错误,因为每次重新渲染父级时都会重置状态
- 通过在钩子中定义 JSX 组件,您可以在需要时取消延迟加载组件的选项。
- 对组件的任何性能优化都需要您使用
useMemo
不会为您提供自定义比较器功能(如 React.memo)的灵活性
另一方面的好处是您可以控制父组件的状态。但是,您仍然可以通过使用受控组件方法来实现相同的逻辑
import React, { useState } from "react";
const Dropdown = Reat.memo((props) => {
const { label, value, updateState, options } = props;
const id = `use-dropdown-${label.replace(" ", "").toLowerCase()}`;
return (
<label htmlFor={id}>
{label}
<select
id={id}
value={value}
onChange={e => updateState(e.target.value)}
onBlur={e => updateState(e.target.value)}
disabled={!options.length}
>
<option />
{options.map(item => (
<option key={item} value={item}>
{item}
</option>
))}
</select>
</label>
);
});
export default Dropdown;
并将其用作
import React, { useState, useEffect } from "react";
import useDropdown from "./useDropdown";
const SomeComponent = () => {
const [animal, updateAnimal] = useState("dog");
const [breed, updateBreed] = useState("");
return (
<div className="search-params">
<form>
<label htmlFor="location">
Location
<input
id="location"
value={location}
placeholder="Location"
onChange={e => updateLocation(e.target.value)}
/>
</label>
<Dropdown label="animal" value={animal} updateState={updateAnimal} options={ANIMALS}/>
<Dropdown label="breed" value={breed} updateState={updateBreed} options={breeds}/>
<button>Submit</button>
</form>
</div>
);
};
export default SomeComponent;
推荐阅读
- java - 为什么我们将 Kotlin 类称为 Java 类?
- react-native - 反应原生:未定义不是一个对象(评估'this.props.navigation.navigate')
- ios - 如何将黑色区域添加到 UIImage ios
- android - 哪些插件要取消选中,哪些插件要在 android studio 中检查以进行颤振开发
- java - 环境变量中的数据库名称
- go - 如何使用 golang 直接绘制到 raspberrypi 屏幕
- bluetooth-lowenergy - 如何开始为我的业务使用 BLE 信标?
- c# - 使用 HttpClient 调用时使用 uri 参数发布(或放置)请求不起作用
- python - 数据包被完全发送 somtimes 和 somtimes 没有完全发送
- android - 安卓用户界面开发