hooks写React组件的5个注意细节详解

目录
  • 正文
  • 01.不需要render的场景下使用useState
  • 02.使用了router.push而非link
  • 03.通过useEffect来处理actions
  • 04.单一职责组件
  • 05.单一职责useEffects

正文

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

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

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

不推荐×

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 都会使组件重新渲染一次,而这个渲染并不是我们需要的,多出来的渲染会使得页面的性能变差,因此我们可以改造一下代码,如下代码:

推荐√如果我们只是单纯的想要一个能在组件声明周期内保存的变量,但是变量的更新不需要组件的重新渲染,我们可以使用 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 (
    &lt;header&gt;
      {isMobile() ? &lt;BurgerButton menuItems={menuItems} /&gt; : &lt;Tabs tabData={menuItems} /&gt;}
    &lt;/header&gt;
  )
}

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

以上就是hooks写React组件的5个注意细节详解的详细内容,更多关于hooks写React组件的资料请关注我们其它相关文章!

(0)

相关推荐

  • React Hooks useReducer 逃避deps组件渲染次数增加陷阱

    目录 前言 自定义 Hooks 简单实现 在组件中使用自定义 Hooks 提前阻止 dispatch 触发 优化后再测试 结论 题外 前言 在快乐使用 React Hooks 开发自定义 Hooks 过程中,使用了 useEffect,useReducer,useRef,useCallback 等官方提供的 Hooks,将一些通用逻辑抽离出来,提高代码复用性. 但在组合使用 useEffect,useReducer,React.memo 时,发生了组件在状态未发生变化时触发渲染,因为此动作发生在

  • React使用高阶组件与Hooks实现权限拦截教程详细分析

    目录 思路 实现 注入权限列表 抽离Context 向页面注入权限列表的HOC 向根组件注入权限 含有权限拦截功能的HOC 无权限时显示的组件 权限拦截HOC组件 测试 用于测试的组件 在组件中使用权限组件 高阶组件是以组件作为参数,返回一个组件的函数.返回的组件把传进去的组件进行功能强化.通过以下示例进一步理解高阶组件. 思路 使用HOC包裹需要权限的页面或组件,并注入唯一的权限签名 用Context保存全局的权限菜单列表,用Provider注入异步获取到的权限菜单 在HOC中用useCont

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

    Hook是React16.8开始新增的特性.虽然React官方文档已经作出了针对React hooks的相关概念的讲解,但是光看官方文档是很难将hooks使用好的,在编写hooks的过程中很容易跳进陷阱和错误.本文总结了5个不好的地方. 01.不需要render的场景下使用useState 在函数组件中我们可以使用useState来管理状态,这使得对状态的管理变得很简单,但是也容易被滥用,我们通过下面的代码样例看下容易忽略的地方. 不推荐× function ClickButton(props)

  • react 组件实现无缝轮播示例详解

    目录 正文 无缝轮播 实现思路 构思使用时代码结构 Carousel组件 CarouselItem组件 完善组件 完成小圆点 正文 需求是做一个无缝轮播图,我说这不是有很多现成的轮子吗?后来了解到他有一个特殊的需求,他要求小圆点需要在轮播图外面,因为现在大部分插件都是将小圆点写在轮播图内部的,这对于不了解插件内部结构的小伙伴确实不知道如何修改. 很久没有写插件的我准备写一个插件(react) 无缝轮播 无缝轮播从最后一张到第一张的过程中不会原路返回,它就像轮子似的,从结束到开始是无缝连接的,非常

  • React三大属性之Refs的使用详解

    refs是React中用来取得某个JSX组件或者某个DOM中的一些状态值的时候,用来获取节点的方法.在React官方的解释中,它的适用范围如下: 管理焦点,文本选择或媒体播放. 触发强制动画. 集成第三方 DOM 库. React文档中再三强调,请不要过度使用refs,所以当我们可以用dom原生对象解决时,尽量不要使用refs 依照之前的写法,首先是给出类组件和函数组件中refs的写法 类组件 在类中,refs有三种方式,目前最常用的是回调的形式使用,分别进行演示 //直接定义refs,已废弃

  • React实现卡片拖拽效果流程详解

    前提摘要: 学习宋一玮 React 新版本 + 函数组件 &Hooks 优先 开篇就是函数组件+Hooks 实现的效果如下: 学到第11篇了 照葫芦画瓢,不过老师在讲解的过程中没有考虑拖拽目标项边界问题,我稍微处理了下这样就实现拖拽流畅了 下面就是主要的代码了,实现拖拽(src/App.js): 核心在于标记当前项,来源项,目标项,并且在拖拽完成时对数据处理,更新每一组数据(useState): /** @jsxImportSource @emotion/react */ // 上面代码是使用e

  • React三大属性之props的使用详解

    上期讲了state,接下来讲讲props.props功能在于组件间通信(父子组件),首先说说在各种组件中的用法: 类组件 //父组件传值 class Father extends React.PureComponent{ render(){ return ( <Son value={"son"} /> ) } } class Son extends React.PureComponent{ render(){ return ( <div>this data is

  • React 三大属性之state的使用详解

    React中很多地方需要用到数据,这在React中被叫做状态,我们需要一个专门管理状态的方法,于是state相关的就诞生了.state应该被要求有两个基本功能,一,能够存储一定的值,从而能被react使用,二,能够再它改变的时候被React监听到并且重新渲染.这里分别介绍一下在类和函数组件中state的写法: 类组件 class ClassComponent extends React.Component{ constructor(props){ super(props) } //可写可不写 r

  • React路由拦截模式及withRouter示例详解

    目录 一.路由拦截 二.路由模式 三.withRouter 一.路由拦截 在前面两篇 路由博客基础上,我们将ReactRouter.js的我的profile路由设置成路由拦截的: <Route path="/profile" render={() => isAuth() ? <Profile/> : <Redirect to="/login"></Redirect> }></Route> 新建Logi

  • react中使用antd及immutable示例详解

    目录 一.react中使用antd组件库 二.Immutable 2.1 深拷贝和浅拷贝的关系 2.2 immutable优化性能方式 2.3 immutable的Map使用 2.4 immutable的List使用 2.5 实际场景formJS 三.redux中使用immutable 一.react中使用antd组件库 运行命令create-react-app antd-react创建新项目: 运行命令npm i antd安装: 使用: import React from 'react' im

  • React事件处理和表单的绑定详解

    目录 一.事件处理 1.1 React事件 1.2 事件对象 1.3 事件传参 1.4 函数组件事件处理 二.表单 一.事件处理 1.1 React事件 React 元素的事件处理和 DOM 元素类似.但是有一点语法上的不同: React 事件绑定属性的命名采用驼峰式写法,而不是小写. 例如onclick属性应该写成onClick. 如果采用 JSX的语法你需要传入一个函数作为事件处理函数,而不是一个字符串(DOM 元素的写法).例如已经定义了一个名为handleClick的函数,应该这样调用:

  • React immer与Redux Toolkit使用教程详解

    目录 1. immer 1.1 setState结合immer使用 1.2 useState结合immer使用 1.3 immer和redux集合 2. Redux Toolkit 1. immer 概述: 它和immutable相似的,实现了操作对象的数据共享,可以优化性能.它实现的原理使用es6的Proxy完成的.小巧,没有像immutable哪样有新数据类型,而是使用js类型. 安装: yarn add immer@9 1.1 setState结合immer使用 简单使用: import

随机推荐