你应该避免的常见React钩子的错误 2021-11-11 默认分类 暂无评论 2467 次阅读 ![](http://www.guobacai.com/usr/uploads/2021/11/3565522977.png) React Hook功能在React 16.8更新中首次引入,由于其功能,在开发者中大受欢迎。它允许你钩住React状态和生命周期功能。而且,有时候,当把它用于高级用例时,它可能是一个挑战。 在这篇文章中,我将讨论5个常见的React Hooks错误,每个开发人员在构建强大的React应用程序时都应该避免。 ## 1. 改变Hooks的调用顺序 > Hooks不应该在循环、条件或嵌套函数中被调用,因为有条件地执行Hooks会导致意外的bug。 避免这种情况,可以确保每次组件渲染时Hooks的调用顺序是正确的。此外,它还有助于在众多的`useState`和`useEffect`调用中保持Hooks的状态一致。 在下面的例子中,我创建了一个基本的React组件,从下拉菜单中选择一个人,并显示所选人员的数据。 `SelectPerson`组件把要检索的人的id作为一个道具,并通过`useEffect()`Hook把个人数据保存在状态变量person中。 ```javascript import { useState, useEffect } from "react"; function SelectPerson({ id }) { if (!id) { return Please Select a Person ; } const [person, setPerson] = useState({ name: "", role: "" }); useEffect(() => { const selectPerson = async () => { const response = await fetch(`/api/persons/${id}`); const selectedPerson = await response.json(); setPerson(selectedPerson); }; selectPerson(); }, [id]); return ( Name: {person.name} Description: {person.role} ); } export default function IndexPage() { const [personId, setPersonId] = useState(""); return ( setPersonId(target.value)}> Select a person David John Tim ); } ``` 乍一看,一切都很好,但你会得到一个控制台错误,因为`useState()`和`useEffect()`不是在每个渲染中都可用。 ![](http://www.guobacai.com/usr/uploads/2021/11/860257960.png) 为了避免这种情况,你可以简单地将返回语句移到Hooks调用之后。 > 所以,永远记得在你的React组件的顶层的任何早期返回之前使用Hooks。 ```javascript import { useState, useEffect } from "react"; function SelectPerson({ id }) { const [person, setPerson] = useState({ name: "", role: "" }); useEffect(() => { const selectPerson = async () => { const response = await fetch(`/api/persons/${id}`); const selectedPerson = await response.json(); setPerson(selectedPerson); }; if (id) { selectPerson(); } }, [id]); if (!id) { return 'Please Select a Person'; } return ( Name: {person.name} Description: {person.role} ); } .... ... ... ``` 2. 在不需要重新渲染的情况下使用`useState` 在功能组件中,你可以使用`useState` Hook进行状态处理。虽然它很直接,但如果使用不当,也会出现意想不到的问题。 例如,假设一个组件有两个按钮。第一个按钮将触发一个计数器,而第二个按钮将根据当前的计数器状态提出请求。 ```javascript import { useState } from "react"; function CounterCalc() { const [counter, setCounter] = useState(0); const onClickCounter = () => { setCounter((c) => c + 1); }; const onClickCounterRequest = () => { apiCall(counter); }; return ( Counter Counter Request ); } export default function IndexPage() { return ( ); } ``` 上述组件可以正常工作。但是,由于我们没有在渲染阶段使用状态,每次点击计数器按钮都会出现不需要的重渲染。 所以,如果你需要在一个组件中使用一个变量,在不触发重新渲染的情况下保留其状态,使用Ref Hook会是一个更好的选择。 ```javascript import { useRef } from "react"; function CounterCalc() { const counter = useRef(0); const onClickCounter = () => { counter.current++; }; const onClickCounterRequest = () => { apiCall(counter.current); }; return ( Counter Counter Request ); } export default function IndexPage() { return ( ); } ``` ## 3. 通过useEffect处理行动 useEffect Hook是处理道具或状态变化相关任务的最酷方法之一。但是,在有些情况下,它可能会使事情变得更加复杂。 假设有一个组件需要一个数据列表并将其渲染到DOM上。 ```javascript import { useState, useEffect } from "react"; function PersonList({ onSuccess }) { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [data, setData] = useState(null); const fetchPersons = () => { setLoading(true); apiCall() .then((res) => setData(res)) .catch((err) => setError(err)) .finally(() => setLoading(false)); }; useEffect(() => { fetchPersons(); }, []); useEffect(() => { if (!loading && !error && data) { onSuccess(); } }, [loading, error, data, onSuccess]); return Person Data: {data}; } export default function IndexPage() { return ( ); } ``` 第一个`useEffect` Hook处理初始渲染时的`apiCall()`。如果所有的条件都满足,第二个`useEffect`将调用`onSuccess()`函数。 然而,动作和被调用的函数之间没有直接联系。因此,我们不能保证这种情况只有在请求成功时才会发生。 为了处理这种情况,我们应该把`onSucces()`函数移到`apiCall()`函数里面,而不使用一个单独的Hook来做这个。 ```javascript import { useState, useEffect } from "react"; function PersonList({ onSuccess }) { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [data, setData] = useState(null); const fetchPersons = () => { setLoading(true); apiCall() .then((fetchedPersons) => { setData(fetchedPersons); onSuccess(); }) .catch((err) => setError(err)) .finally(() => setLoading(false)); }; useEffect(() => { fetchPersons(); }, []); return Person Data: {data}; } export default function IndexPage() { return ( ); } ``` ## 4. 使用过时的状态 如果你在代码的后续行中多次修改`useState`,其结果可能会让你吃惊。 ```javascript import { useState } from "react"; function Counter({ onSuccess }) { const [count, setCount] = useState(0); console.log(count); const increaseCount = () => { setCount(count + 1); setCount(count + 1); setCount(count + 1); }; return ( <> Increase Counter: {count} > ); } export default function IndexPage() { return ( ); } ``` 在上面的例子中,计数器组件增加了状态变量`count`的值。由于`encreateCount()`函数调用了`setCount()`函数3次,你可能认为点击一次按钮会使`count`值增加3。 但是,每次点击按钮只会增加1。 最初调用`setCount(count + 1)`会适当地增加计数,因为`count + 1 = 0 + 1 = 1`。同样地,随后两次对`setCount(count + 1)`的调用也将计数设为1,因为它使用的是计数的过时状态。 > 发生这种情况是因为状态的值只有在下次渲染时才会被更新。 这个过时的状态问题可以通过更新状态的功能方式来解决。 ```javascript import { useState } from "react"; function Counter({ onSuccess }) { const [count, setCount] = useState(0); console.log(count); const increaseCount = () => { setCount(count => count + 1); setCount(count => count + 1); setCount(count => count + 1); }; return ( <> Increase Counter: {count} > ); } export default function IndexPage() { return ( ); } ``` ## 5. 缺少useEffect的依赖 `useEffect Hook`是`React`中最常用的`Hooks`之一,默认情况下,它总是在每次重新渲染时运行。然而,这种在每次渲染后清理或应用效果的行为会导致性能问题。 我们可以通过向`useEffect Hook`传递一个依赖数组来避免这些不必要的渲染。 `useEffect Hook`接受依赖性数组作为第二个参数,它确保只有在数组中的元素在重新渲染之间发生变化时才会运行效果。 ```javascript useEffect(() => { showCount(count); }, [count]); // Only re-run the effect if count changes in the preceding render ``` 让我们考虑下面的例子。乍看之下,这似乎是一个工作良好的解决方案。 ```javascript import { useState, useEffect} from "react"; function Counter() { const [count, setCount] = useState(0); const showCount = (count) => { console.log("Show Count", count); }; useEffect(() => { showCount(count); }, []); return ( <> Hello..!! Counter: {count} > ); } export default function IndexPage() { return ( ); } ``` 但是,有一个问题,你可以在`DevTools`控制台找到下面的警告。 ![](http://www.guobacai.com/usr/uploads/2021/11/3963312310.png) > 注意:虽然它要求你删除或保留依赖阵列,但你不应该删除它,因为这将是一个欺骗React关于依赖的黑客。 为了正确地解决这个问题,你需要确保数组包含组件范围内的所有依赖值,这些依赖值可能随时间变化,并被效果使用。 否则,你的代码将使用先前渲染中的值,而这些值已不再有效。 ```javascript import { useState, useEffect} from "react"; function Counter() { const [count, setCount] = useState(0); const showCount = (count) => { console.log("Show Count", count); }; useEffect(() => { showCount(count); }, [count]); return ( <> Hello..!! Counter: {count} > ); } export default function IndexPage() { return ( ); } ``` ## 用独立的组件构建,以提高速度和规模 与其构建单一的应用程序,不如先构建独立的组件,并将它们组合成功能和应用程序。它使开发更快,并帮助团队建立更一致和可扩展的应用程序。 Bit为构建独立组件和合成应用提供了很好的开发者体验。许多团队开始通过独立组件来构建他们的设计系统或微前端。 [试试吧→](http://www.guobacai.com/index.php/go/url-256/ "试试吧→") ![](http://www.guobacai.com/usr/uploads/2021/11/641978669.png) 一个独立的源控制和共享的 "卡 "组件。右边是=>它的依赖关系图,由Bit自动生成的。 ## 最后的思考 `React Hooks`是可重用的、干净的、简单的函数,可以被纳入你的工作流程中。此外,它们为我们提供了许多不同用途的选择,所以我们不必从头开始构建它们。 但是,开发人员在使用`React Hooks`的高级和自定义功能时,往往会犯一些重大错误。在这篇文章中,我讨论了开发者在使用`React Hooks`时应该避免的5个这样的常见错误。 我希望你能在你的下一个`React`项目中使用它们,如果你有任何建议,请在评论区分享它们。 标签: react, 组件, const, 功能, 处理, import, 函数, 错误, usestate, return, hooks
评论已关闭