Hook是React16.8开始新增的特性。固然React官方文档已经作出了针对React hooks的相关观念的讲授,可是光看官方文档是很难将hooks利用好的,在编写hooks的进程中很容易跳进陷阱和错误。本文总结了5个欠好的处所。
01.不需要render的场景下利用useState在函数组件中我们可以利用useState来打点状态,这使得对状态的打点变得很简朴,可是也容易被滥用,我们通过下面的代码样例看下容易忽略的处所。
不推荐×
function ClickButton(props){ const [count, setCount] = setState(0) const onClickCount = () => { setCount((c) => c + 1) } const onClickRequest = () => { apiCall(count) } return ( <div> <button onClick={onClickCount}>Click</button> <button onClick={onClickRequest}>Submit</button> </div> ) }
问题地址:仔细看上面的代码,乍一看其实也没什么问题,点击按钮更新 count。可是问题也就出在这里,我们的 return 部门并没有用到 count 状态,而每次 setCount 城市使组件从头渲染一次,而这个渲染并不是我们需要的,多出来的渲染会使得页面的机能变差,因此我们可以改革一下代码,如下代码:
推荐√
假如我们只是纯真的想要一个能在组件声明周期内生存的变量,可是变量的更新不需要组件的从头渲染,我们可以利用 useRef 钩子。
function ClickButton(props){ const count = useRef(0) const onClickCount = () => { count.current++ } const onClickRequest = () => { apiCall(count.current) } return ( <div> <button onClick={onClickCount}>Click</button> <button onClick={onClickRequest}>Submit</button> </div> ) }
02.利用了router.push而非link在React SPA应用中,我们用react-router来处理惩罚路由的跳转,我们很常常在组件中写了一个按钮,通过点击按钮的事件来处理惩罚路由的跳转,如下代码:
不推荐×
function ClickButton(props){ const history = useHistory() const onClickGo = () => { history.push('/where-page') } return <button onClick={onClickGo}>Go to where</button> }
问题地址:尽量上述代码可以正常事情,可是却不切合Accessibility(易会见性设计)的要求,此类按钮并不会被屏幕阅读器看成一个可以跳转的链接。因此我们可以改革一下代码,如下代码:
推荐√
function ClickButton(props){ return <Link to="/next-page"> <span>Go to where</span> </Link> }
03.通过useEffect来处理惩罚actions有时候,我们只想在 React 更新 DOM 之后运行一些特另外代码。好比发送网络请求,手动改观 DOM,记录日志。
不推荐×
function DataList({ onSuccess }) { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [data, setData] = useState(null); const fetchData = () => { setLoading(true); callApi() .then((res) => setData(res)) .catch((err) => setError(err)) .finally(() => setLoading(false)); }; useEffect(() => { fetchData(); }, []); useEffect(() => { if (!loading && !error && data) { onSuccess(); } }, [loading, error, data, onSuccess]); return <div>Data: {data}</div>; }
问题地址:上面的代码利用了两个useEffect ,第一个用来请求异步数据,第二个用来挪用回调函数。在第一个异步请求数据乐成,才会触发第二个 useEffect 的执行,可是,我们并不能完全担保,第二个 useEffect 的依赖项完全受控于第一个 useEffect 的乐成请求数据。因此我们可以改革一下代码,如下代码:
推荐√
function DataList({ onSuccess }) { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [data, setData] = useState(null); const fetchData = () => { setLoading(true); callApi() .then((res) => { setData(res) onSuccess() }) .catch((err) => setError(err)) .finally(() => setLoading(false)); }; useEffect(() => { fetchData(); }, []); return <div>Data: {data}</div>; }
04.单一职责组件什么时候该把一个组件分成几个更小的组件?如何构建组件树?在利用基于组件的框架时,所有这些问题天天城市呈现。然而,设计组件时的一个常见错误是将两个用例组合成一个组件。
不推荐×
function Header({ menuItems }) { return ( <header> <HeaderInner menuItems={menuItems} /> </header> ); } function HeaderInner({ menuItems }) { return isMobile() ? <BurgerButton menuItems={menuItems} /> : <Tabs tabData={menuItems} />; }
问题地址:上面的代码通过这种要领,组件HeaderInner试图同时成为两个差异的对象,一次做不止一件工作并不是很抱负。另外,它还使得在其他处所测试或重用组件变得越发坚苦。因此我们可以改革一下代码,如下代码:
推荐√
将条件晋升一级,可以更容易地看到组件的用途,而且它们只有一个职责,即<Tabs/>或<BurgerButton/>,而不是试图同时成为两个差异的对象。