React 新特性React Hooks

你还在为该使用无状态组件(Function)还是有状态组件(Class)而烦恼吗?
——拥有了hooks,你再也不需要写Class了,你的所有组件都将是Function。
你还在为搞不清使用哪个生命周期钩子函数而日夜难眠吗?
——拥有了Hooks,生命周期钩子函数可以先丢一边了。
你在还在为组件中的this指向而晕头转向吗?
——既然Class都丢掉了,哪里还有this?你的人生第一次不再需要面对this。
这样看来,说React Hooks是今年最劲爆的新特性真的毫不夸张。如果你也对react感兴趣,或者正在使用react进行项目开发,答应我,请一定抽出至少30分钟的时间来阅读本文好吗?所有你需要了解的React Hooks的知识点,本文都涉及到了,相信完整读完后你一定会有所收获。

一个最简单的Hooks

首先让我们看一下一个简单的有状态组件:
blockchain
我们再来看一下使用hooks后的版本:
blockchain
是不是简单多了!可以看到, Example变成了一个函数,但这个函数却有自己的状态(count),同时它还可以更新自己的状态(setCount)。这个函数之所以这么了不得,就是因为它注入了一个hook-- useState,就是这个hook让我们的函数变成了一个有状态的函数。

除了 useState这个hook外,还有很多别的hook,比如 useEffect提供了类似于 componentDidMount等生命周期钩子的功能, useContext提供了上下文(context)的功能等等。

Hooks本质上就是一类特殊的函数,它们可以为你的函数型组件(function component)注入一些特殊的功能。咦?这听起来有点像被诟病的Mixins啊?难道是Mixins要在react中死灰复燃了吗?当然不会了,等会我们再来谈两者的区别。总而言之,这些hooks的目标就是让你不再写class,让function一统江湖。

React为什么要搞一个Hooks?
想要复用一个有状态的组件太麻烦了!

我们都知道react都核心思想就是,将一个页面拆成一堆独立的,可复用的组件,并且用自上而下的单向数据流的形式将这些组件串联起来。但假如你在大型的工作项目中用react,你会发现你的项目中实际上很多react组件冗长且难以复用。尤其是那些写成class的组件,它们本身包含了状态(state),所以复用这类组件就变得很麻烦。

那之前,官方推荐怎么解决这个问题呢?答案是:渲染属性(Render Props)和高阶组件(Higher-Order Components)。我们可以稍微跑下题简单看一下这两种模式。

渲染属性指的是使用一个值为函数的prop来传递需要动态渲染的nodes或组件。如下面的代码可以看到我们的 DataProvider组件包含了所有跟状态相关的代码,而 Cat组件则可以是一个单纯的展示型组件,这样一来 DataProvider就可以单独复用了。
blockchain
虽然这个模式叫Render Props,但不是说非用一个叫render的props不可,习惯上大家更常写成下面这种:
blockchain
高阶组件这个概念就更好理解了,说白了就是一个函数接受一个组件作为参数,经过一系列加工后,最后返回一个新的组件。看下面的代码示例, withUser函数就是一个高阶组件,它返回了一个新的组件,这个组件具有了它提供的获取用户信息的功能。
blockchain
以上这两种模式看上去都挺不错的,很多库也运用了这种模式,比如我们常用的React Router。但我们仔细看这两种模式,会发现它们会增加我们代码的层级关系。最直观的体现,打开devtool看看你的组件层级嵌套是不是很夸张吧。这时候再回过头看我们上一节给出的hooks例子,是不是简洁多了,没有多余的层级嵌套。把各种想要的功能写成一个一个可复用的自定义hook,当你的组件想用什么功能时,直接在组件里调用这个hook即可。
blockchain
Hooks让我们的函数组件拥有了类似类组件的特性,比如local state、lifecycle,而且还解决了上面提到的一系列问题,它是如何解决这些问题的,下面会在一一指出。首先来快速的看看Hoos的使用,这里讲最主要的两个 Hooks :useState 和 useEffect。先看一个你可能看过很多遍的例子:

import { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });
  return (
      <p> {count} </p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
  );
}
useState

useState 这个方法可以为我们的函数组件带来 local state,它接收一个用于初始 state 的值,返回一对变量;

const [count, setCount] = useState(0);

// 等价于
var const = useState(0)[0]; // 该state
var setConst = useState(0)[1]; // 修改该state的方法
useEffect

useEffect 可以利用我们组件中的 local state 进行一些带有副作用的操作

useEffect(() => {
  document.title = `You clicked ${count} times`;
});

useEffect 中还可以通过传入第二个参数来决定是否执行里面的操作来避免一些不必要的性能损失,只要第二个参数数组中的成员的值没有改变,就会跳过此次执行。如果传入一个空数组 [ ],那么该 effect 只会在组件 mount 和 unmount 时期执行。

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // 如果count没有改变,就跳过此次执行

useEffect 中还可以通过让函数返回一个函数来进行一些清理操作(clean up),比如取消订阅等;

useEffect(() => {
  api.subscribe(theId);
  return () => {
      api.unsubscribe(theId)    //clean up
  }
});

useEffect 什么时候执行? 它会在组件 mount 和 unmount 以及每次重新渲染的时候都会执行,也就是会在 componentDidMount、componentDidUpdate、componentWillUnmount 这三个时期执行。

清理函数(clean up)什么时候执行? 它会在前一次 effect执行后,下一次 effect 将要执行前,以及 Unmount 时期执行

注意事项:

我们只能在 函数组件 中使用 Hooks,我们也可以在一个组件中使用多组 Hooks。比如:

function FriendStatusWithCounter(props) {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    API.subscribe(props.friend.id);
    return () => {
      API.unsubscribe(props.friend.id);
    };
  });

  return isOnline
}

但是这里有一点需要我们注意的就是 我们只能在顶层代码(Top Level)中调用 Hooks,不能在循环或判断语句等里面调用,这样是为了让我们的 Hooks 在每次渲染的时候都会按照 相同的顺序 调用,因为这里有一个跟关键的问题,那就是 useState 需要依赖参照第一次渲染的调用顺序来匹配对于的state,否则 useState 会无法正确返回它对于的state。

Hooks 解决的问题

好了,知道了 Hooks 基本使用后,我们就可以来了解 Hooks 是怎么解决 react 长期存在的问题的。

如何解决 状态有关的逻辑(stateful logic) 的重用和共享问题。
过去对于类似问题的解决方案主要有两个:

  • Render Props 通过props接受一个返回react element的函数,来动态决定自己要渲染的结果;
<DataProvider render={data => (
 <h1>Hello {data.target}</h1>
)}/>
  • 还有就是Higher-Order Components 以一种类似 工厂模式 的方式去生产出具有相同或类似逻辑的组件。
function getComponent(WrappedComponent) {

 return class extends React.Component {
   constructor(props) {
     super(props);
   }
   componentDidMount() {
     // doSomething
   }
   componentWillUnmount() {
     // doSomething
   }
   render() {
     return <WrappedComponent {...this.props} />;
   }
 };
}

但是无论是哪一种方法都会造成组件数量增多,组件树结构的修改,而且有可能出现组件嵌套地狱(wrapper hell)的情况。现在 React 通过 custom Hooks 来解决这个问题。

custom Hooks
custom Hooks 并不是一个api,而是一个规则。具体实现就是通过一个函数来封装跟状态有关的逻辑(stateful logic),将这些逻辑从组件中抽取出来。在这个函数中我们可以使用其他的 Hooks,也可以单独进行测试,甚至将它贡献给社区。

import { useState, useEffect } from 'react';

function useCount() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });
  
  return count
}

比如上面的一个例子,他就是一个 custom Hooks,提取了对 count 的操作。这里需要遵循一个约定,命名要用 use*,这是为了方便我们区分,利于我们维护。可以看到他其实就是一个函数,我们可以在现有的所有其他组件中引用它

function CountStatus() {
  const count = useCount();
  return count;
}

这里的核心概念就是将逻辑提取出来封装在 custom Hooks,然后可以在任何的其他组件中共享这部分逻辑,也可以贡献给社区。所以我也预测在不久的将来,会出现很多的充满想象力的各种用途的 custom Hooks 在社区中出现,极大的提高我们的开发效率。

具有复杂逻辑的组件的开发和维护
前面我们也提到,我们的组件可能会随着开发的进行变得越来越复杂,要处理越来越多的 local State,那么在组件的生命周期函数中就会充斥着各种互不相关的逻辑,这里需要引入官方的比较复杂的例子,先看基于以前类组件的情况:

class FriendStatusWithCounter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0, isOnline: null };
    this.handleStatusChange = this.handleStatusChange.bind(this);
  }

  componentDidMount() {
    document.title = `You clicked ${this.state.count} times`;
    ChatAPI.subscribeToFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }

  componentDidUpdate() {
    document.title = `You clicked ${this.state.count} times`;
  }

  componentWillUnmount() {
    ChatAPI.unsubscribeFromFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }

  handleStatusChange(status) {
    this.setState({
      isOnline: status.isOnline
    });
  }
  // ...

经过 Hook 改造后:

function FriendStatusWithCounter(props) {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }
  // ...
}

状态和相关的处理逻辑可以按照功能进行划分,不必散落在各个生命周期中,大大降低了开发和维护的难度。除了这几个hooks还有其他额外的hooks,在此继续了解 Hooks API Reference

伴随 Hooks 的一些思考

hooks让我们的函数组件的功能得到了扩充,拥有了和类组件相似的功能,甚至避免了类组件存在的各种问题,那么就会出现各种的疑问,比如

  • Hooks 引进后, 函数组件 和 类组件 该如何选择?官方关于类似的问题的答复是:

官方的目标是尽可能快的让 Hooks 去覆盖所有的类组件案例,但是现在 Hooks 还处于一个非常早的阶段,各种调试工具、第三方库等都还没有做好对 Hooks 的支持,而且目前也没有可以取代类组件中 getSnapshotBeforeUpdate 和 componentDidCatch 生命做起的 Hooks,不过很快会加上他们。总的来时就是鼓励大家在以后使用 Hooks,对于已存在的类组件不必大规模的去重写,Hooks及Hooks的生态会继续完善,请期待

  • Hooks 是否可以代替 render-props 和 higher-order components ?前面我们也提到,hooks可以解决后者带来的各种问题,那么 hooks 是否可以代替后者呢?官方的回答:

在大多数案例下,hooks 足够应付且更适合,所以优先考虑 hooks。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
我们的app名字叫做'老师去哪儿了',顾名思义,主要是方便同学们更快更有效率的找到自己想要找到的老师。 我们利用二维码的便利性,为每一位办公室制定特定的二维码门牌,老师可以利用留言功能很方便的将今天的课程或者自己的每日行程添加到我么么的数据库中,学生只要轻松一扫便可以和很方便的了解到你所需要找的老师的信息,下面更加详细的介绍一下我们的软件。 (1)登陆设计: 最开始的是登陆界面,在登陆界面上我们可以快速注册,注册时会有两个选项,你可以根据自己的身份选择不同的选项,老师或者是学生;如果你忘记了密码,还可以通过邮箱找回。成功登陆后,就可看到有三个界面可以选择。 (2)、留言设计: 这个界面主要用于老师留言,老师可以根据自己的行程安排来留言,从而更方便别人来访。 (2)、发现设计: 可分为生成二维码和扫描二维码 生成二维码: 如果你是老师,你可将自己的办公室号,桌号或者是自己的个人基本信息输入,使其生成对应的二维码。要特殊说明的一点,生成的二维码图片你可自由选择是否保存在本地。 扫描二维码: 你可选择用摄像头扫描或是直接从图片中扫描;扫描完的结果同时也有两种选择,查询或者是进入网页。 (3)、个人信息设计: 其中包括了通知中心,推荐给朋友还有退出账号。推荐给朋友,为了迎合大多数人的不同的习惯,我们提供了多种平台的选择分享。 二维码现在已经充斥了我们的生活,然而现在对二维码的开发还仅限于一些基本的生成扫描,或者用于宣传手段,其实只要你有想法,相信二维码的使用可以更加广泛,更加有创意。如果你有兴趣,欢迎来交流呦!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值