-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
精读《React Hooks 最佳实践》 #202
Comments
文章链接在哪? |
延到本周了,欢迎讨论。 |
关于 defaultProps, 现在(准确的说是未来)官方的推荐其实就是解构的默认参数来取代 defaultProps,未来的React会去掉对defaultProps的支持以简化内部流程,至于里面提到默认的object参数会每次会重新创建的问题,目前的推荐方案在外面创建默认值 https://github.com/reactjs/rfcs/blob/createlement-rfc/text/0000-create-element-changes.md#deprecate-defaultprops-on-function-components 还有,这个最佳实践有太多的过度优化的地方,比如 useCallback 以及 useMemo,尤其是 useMemo来模拟React.memo,这种应该算是anti-pattern吧,useMemo不是为了这种场景设计的,未来React有可能直接将memo內建,那现在这种方式就会变得毫无意义 |
+1 |
useEffect(() => {
props.onChange(props.id)
}, [props.id]) |
这么做会导致 |
然而加上了也会出现死循环,这貌似并没有起到保护新人的作用。而反设计要求父组件来妥协,这是在给自己和使用者挖坑。 所以 exhaustive-deps 检查其实是不完美的,因为怎么用存在二义性,如果不理解 hooks 检不检查都会踩坑。更好的方式是把复杂的 hooks 拆分通过组合方式构建,每个 hooks 目的单一明确。 如果非要用 exhaustive-deps 检查,一个折中的方式是用 ref const onChangeRef = useRef(props.onChange)
onChangeRef.current = props.onChange
useEffect(() => {
onChangeRef.current(props.id)
}, [props.id]) |
其实这也不能完全算是一个缺陷吧,毕竟useEffect在很大程度上可以当做类组件中 在函数式组件中使用useEffect来实现一些响应式功能的逻辑,基本可以在类组件中转化为分散在不同生命周期中的逻辑(当然对于框架底层逻辑来说确实是有明显差异的)。 而在生命周期中如果你想要实现同样的功能,其实一样会遇到这些问题。这是引用类型天然的问题,而不是lint rule的缺陷,只是因为hooks提供了一个快车道导致这类问题暴露的更频繁而已。 |
我是指 exhaustive-deps 检查是不完美的,而没有否定 |
我觉得对于使用了hooks的函数式组件来说,真正存在本质区别的,其实是因为 |
这又是另外一个话题了。目前(从您第一个回复开始)讨论的是,权衡需不需要引用不完美的检查规则,并为此编写额外代码。这不是 |
但问题是,你不能保证props的提供者每次都能提供“同样功能”的函数,毕竟使用了hooks的组件无法确认它会在什么样的场景下被使用。可能父组件组件在给子组件传递函数时采用了一个三目运算符来使得子组件在父组件持有不同状态时执行不同的功能。 |
这并不影响我以上提供的两种解决方式。 |
我不否认这一点,但这条lint就我个人来看也确实很难做的更完美了,毕竟这是运行时的问题了。我还是觉得,开启lint规则至少可以降低1/3甚至一半的常见问题(而且也可以算是一种好习惯),然后可以采取您上面提出的这种通过useRef的方式来避免剩下一半的常见问题。 |
用代码假设业务,有一组图片信息需要编辑,怎么才能更简单的防止触发其他组件的re-render,按道理bind会返回一个新的函数,类似双箭头函数,会每次进行re-render。 var zone = {
title: 'space1',
dataList: [
{ img:'xxx1.png', title: 'p1' },
{ img:'xxx2.png', title: 'p2' },
]
}
// 合并配置
const mergeMultipleOption = useCallback((attr, val) => {
setMultipleOption(pre => ({
...pre,
[attr]: value,
}));
}, []);
// attr 作为预留 mergeMultipleOption 第一个参数
const triggerToMergeMultipleOption = useCallback(
(attr) => mergeMultipleOption.bind(null, attr),
[]);
// 更新list
const updateImg = useCallback((index, value) => {
const newImgList = dataList.map((data, idx) => {
if (idx === index) {
return {
...data,
img: value,
};
}
return data;
});
triggerToMergeMultipleOption('dataList')(newImgList);
}, [dataList]);
// index 预留下标
const triggerToUpdateImg = useCallback(
(index) => updateImg.bind(null, index),
[zone.dataList],);
return (
zone.dataList.map((item,idx=>(
<Upload type="file" onChange={triggerToUpdateImg(index)} />
))
) |
可以把 index 往下传, const [zone, setZone] = useState({
title: 'title',
dataList: [],
});
const ref = useRef();
// 每次渲染实时更新数据
ref.current = zone;
// 回调 index, 不用对每个元素 bind
const handleUpload = useCallback((img, index) => {
// 通过 ref 引用实时数据
const { dataList } = ref.current;
const newData = { ...dataList[index], img };
const newDataList = Object.assign([], dataList, { [index]: newData });
const newZone = {...ref.current, dataList: newDataList};
setZone(newZone);
}, []);
return zone.dataList.map((item, index) => (
// Upload 需要支持 name,或者封装一下
<Upload name={index} value={item} onChange={handleUpload} />
)); |
@ascoders 请问文章中的useAsync这个hooks是哪里的,在react文档中没有找到 |
本周的精读是笔者总结出的 Hooks 最佳实践经验,欢迎讨论。
The text was updated successfully, but these errors were encountered: