React新特性-Hooks之自定义Hook(十完结)

自定义Hook可以将一些逻辑抽离至可复用的函数里

系列目录

将复用的逻辑提取成自定义Hook

当我们学习使用Effect Hook时,我们写了这么个组件,该组件显示一条消息,指示朋友是在线还是离线

import React, { useState, useEffect } from 'react';

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

假如现在聊天应用还有一个联系人列表,我们想要给在线用户的名字搞绿,我们可以将上面类似的逻辑复制并粘贴到FriendListItem组件中,但这很丑陋。

import React, { useState, useEffect } from 'react';

function FriendListItem(props) {
  const [isOnline, setIsOnline] = useState(null);

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}

所以我们想要做的是在FriendStatusFriendListItem之间复用这段逻辑。

以前我们是通过render propHOC解决这个问题的, 现在我们看看使用Hook是如何解决的

当我们想要在两个JS函数之间共享一段逻辑时,我们会将这段逻辑提取成另一个函数。因为函数组件和Hook也是函数,所以这个思想同样适用于它们两个。

自定义Hook是一个JavaScript函数,其名称必须以“use”开头,可以调用其他Hook。 例如,下面的useFriendStatus是我们的第一个自定义Hook:

import React, { useState, useEffect } from 'react';

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}

这里面没有任何新内容。 逻辑是从上面的组件中复制出来的。和在组件中使用Hook一样,确保只在自定义Hook的顶层无条件地调用其他Hook。

FriendStatus Hook的功能是订阅friend的状态。它将friendID作为参数,并返回此朋友是否在线

使用自定义Hook

// FriendStatus组件
function FriendStatus(props) {
  const isOnline = useFriendStatus(props.friend.id);

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

//FriendListItem组件
function FriendListItem(props) {
  const isOnline = useFriendStatus(props.friend.id);

  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}

自定义Hook只复用逻辑而不共享状态。所有状态和副作用完全相互独立。因为我们是直接调用useFriendStatus 的,所以从React的视角来看组件不过是调用了useState and useEffect而已。经过前面的学习我们已经知道,在一个组件中多次调用useState和useEffect,它们是完全独立的。

简易版useReducer

Custom Hooks提供了以前在React组件中无法实现的逻辑共享。您可以编写涵盖各种用例的自定义Hook,如表单处理,动画,声明订阅,计时器等等。更重要的是,您可以构建与React的内置功能一样易于使用的Hook。

上一章我们已会使用过useReducer,这一章我们用自定义Hook实现一个简易版的useReducer

function useReducer(reducer, initialState) {
  const [state, setState] = useState(initialState);

  function dispatch(action) {
    const nextState = reducer(state, action);
    setState(nextState);
  }

  return [state, dispatch];
}

//使用useReducer
function Todos() {
  const [todos, dispatch] = useReducer(todosReducer, []);

  function handleAddClick(text) {
    dispatch({ type: 'add', text });
  }

  // ...
}

文章作者: jackie chen
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 jackie chen !
评论
 上一篇
Docker-基础(一) Docker-基础(一)
Docker基础(一) Docker是一个开源的应用容器引擎,基于go语言并遵从Apache2.0协议开源。 Docker可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟
2020-01-03
下一篇 
React新特性-Hooks之useReducer(九) React新特性-Hooks之useReducer(九)
useReducer是React提供的一个高级Hook,它不像useEffect、useState、useRef等必须的hook一样,没有useReducer我们也可以正常完成需求的开发,但useReducer可以使我们的代码具有更好的可
2019-07-28
  目录