前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >同学,请专业点,用Hooks解耦UI组件吧

同学,请专业点,用Hooks解耦UI组件吧

作者头像
公众号@魔术师卡颂
发布2020-12-11 11:47:07
6590
发布2020-12-11 11:47:07
举报
文章被收录于专栏:魔术师卡颂

!! 文章系翻译,原文见阅读原文

你肯定看过(或写过)这样的渲染模式:

  1. 通过AJAX请求数据时渲染一个loading占位图标
  2. 当数据返回后重新渲染组件

让我们一个使用Fetch API的简单例子:

代码语言:javascript
复制
import React, { useState, useEffect } from 'react';

const SomeComponent = (props) => {
  const [someData, setSomeData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    setLoading(true);
    fetch('/some-data')
      .then(response => response.json())
      .then(data => setSomeData(data))
      .catch(error => setError(error))
      .finally(() => setLoading(false));
  }, []);
  
  return (
    <React.Fragment>
      {loading && <div>{'Loading...'}</div>}
      {!loading && error && <div>{`Error: ${error}`}</div>}
      {!loading && !error && someData && <div>{/* INSERT SOME AMAZING UI */}</div>}
    </React.Fragment>
  );
};

当我们的应用逐渐庞大,假设有n个组件要使用同样的数据。

为了减少重复请求,我决定使用LocalStorage缓存服务端数据。

这是否意味着同样的渲染逻辑要重复写n次呢?

解耦数据请求

怎么可能,让我们将数据请求部分抽离为一个自定义hook——useSomeData

代码语言:javascript
复制
import React, { useState, useEffect } from 'react';

const useSomeData = () => {
  const cachedData = JSON.parse(localStorage.getItem('someData'));
  const [someData, setSomeData] = useState(cachedData);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    if (!someData) {
      setLoading(true);
      fetch('/some-data')
        .then(response => response.json())
        .then(data => {
          localStorage.setItem('someData', JSON.stringify(data));
          return setSomeData(data);
        })
        .catch(error => setError(error))
        .finally(() => setLoading(false));
    }
  }, []);
  
  return { someData, loading, error };
};

使用useSomeData看起来像这样:

代码语言:javascript
复制
const SomeComponent = (props) => {
  const { someData, loading, error } = useSomeData();
  return (
    <React.Fragment>
      {loading && <div>{'Loading...'}</div>}
      {!loading && error && <div>{`Error: ${error}`}</div>}
      {!loading && !error && someData && <div>{/* INSERT SOME AMAZING UI */}</div>}
    </React.Fragment>
  );
};

const AnotherComponent = (props) => {
  const { someData, loading, error } = useSomeData();
  return (
    <React.Fragment>
      {loading && <div>{'Loading...'}</div>}
      {!loading && error && <div>{`Error: ${error}`}</div>}
      {!loading && !error && someData && <div>{/* INSERT ANOTHER AMAZING UI */}</div>}
    </React.Fragment>
  );
};

复用代码挺棒的,但就仅此而已吧?

定制数据请求

我们的应用越来越复杂,我决定上Redux

此时只需要简单的修改下useSomeData,完全不需要改动业务组件:

代码语言:javascript
复制
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { selectSomeData } from 'path/to/data/selectors';
import { fetchSomeData } from 'path/to/data/action';

const useSomeData = () => {
  const dispatch = useDispatch();
  const someData = useSelector(selectSomeData);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    if (!someData) {
      setLoading(true);
      dispatch(fetchSomeData())
        .catch(error => setError(error))
        .finally(() => setLoading(false));
    }
  }, []);
  
  return { someData, loading, error }; 
};

某天我决定赶时髦上GraphQL。同样,只需要简单修改useSomeData而无需改动业务组件:

代码语言:javascript
复制
import { gql, useQuery } from '@apollo/client';

const FETCH_SOME_DATA = gql`
  fetchSomeData {
    data {
      # some fields
    }
  }
`;

const useSomeData = () => {
  const { data, loading, error } = useQuery(FETCH_SOME_DATA);
  return { someData: data, loading, error }; 
};

每当我自愿(或被迫)修改数据请求/状态管理部分时,只需要修改对应的hook就行。

就像经典的依赖倒置原则(SOLID中的D)。尽管并非面向对象,但我们定义了一个抽象接口,并基于其实现了该接口的类。

useSomeData实际上为使用他的业务组件提供了一个接口。

开发者不需要关心useSomeData的实现原理,只需要关注接收到的数据、加载状态、错误信息即可。

理论上来说,只要定义合适的接口,就能将UI从数据层解耦出来,并随时迁移到任何数据层上。

点击这里留言

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-12-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 魔术师卡颂 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 解耦数据请求
  • 定制数据请求
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档