最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 使用hooks写React组件注意的5个地方

    正文概述 掘金(forrest酱)   2021-04-05   454

    使用hooks写React组件注意的5个地方 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/>,而不是试图同时成为两个不同的东西。

    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(() => {
        fetchData();
        updateBreadcrumbs();
      }, [location.pathname]);
    
      return (
        <div>
          <BreadCrumbs />
        </div>
      );
    }
    

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


    起源地下载网 » 使用hooks写React组件注意的5个地方

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元