使用hooks写React组件注意的5个地方

搜狗截图20210405114309.png Hook是React16.8开始新增的特性。虽然React官方文档已经做出了针对React hooks的相关概念的讲解,可是光看官方文档是很难将hooks使用好的,在编写hooks的过程当中很容易跳进陷阱和错误。本文总结了5个很差的地方。react

01.不须要render的场景下使用useState

在函数组件中咱们可使用useState来管理状态,这使得对状态的管理变得很简单,可是也容易被滥用,咱们经过下面的代码样例看下容易忽略的地方。api

不推荐×数组

function ClickButton(props){
  const [count, setCount] = useState(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 都会使组件从新渲染一次,而这个渲染并非咱们须要的,多出来的渲染会使得页面的性能变差,所以咱们能够改造一下代码,以下代码:markdown

推荐√
若是咱们只是单纯的想要一个能在组件声明周期内保存的变量,可是变量的更新不须要组件的从新渲染,咱们可使用 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来处理路由的跳转,咱们很常常在组件中写了一个按钮,经过点击按钮的事件来处理路由的跳转,以下代码: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,记录日志。oop

不推荐×

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/>,而不是试图同时成为两个不一样的东西。

function Header(props) {
  return (
    <header> {isMobile() ? <BurgerButton menuItems={menuItems} /> : <Tabs tabData={menuItems} />} </header>
  )
}
复制代码

05.单一职责useEffects

经过对比componentWillReceivePropscomponentDidUpdate方法,才认识到userEffect的美丽。可是没有稳当使用useEffect也是容易出问题的。

不推荐×

function Example(props) {
  const location = useLocation();
  const fetchData = () => {
    /* Calling the api */
  };

  const updateBreadcrumbs = () => {
    /* Updating the breadcrumbs*/
  };

  useEffect(() => {
    fetchData();
    updateBreadcrumbs();
  }, [location.pathname]);

  return (
    <div> <BreadCrumbs /> </div>
  );
}
复制代码

问题所在:上面的useEffect同时触发了两个反作用,可是并不都是咱们须要的反作用,所以咱们能够改造一下代码,以下代码:

推荐√
将两个反作用从一个useEffect中分离出来。

function Example(props) {
  const location = useLocation();

  const fetchData = () => {
    /* Calling the api */
  };

  const updateBreadcrumbs = () => {
    /* Updating the breadcrumbs*/
  };

  useEffect(() => {
    updateBreadcrumbs();
  }, [location.pathname]);

  useEffect(()=>{
    fetchData();
  },[])
  
  return (
    <div> <BreadCrumbs /> </div>
  );
}
复制代码

参考:Five common mistakes writing react components (with hooks) in 2020

相关文章
相关标签/搜索