06月09, 2019

admin模板(2)

这周终于把剩下的admin模板写的差不多了,所以继续来记录一下写代码中碰到的一些问题,算是一些总结吧!

alt

面包导航

这一块其实可以抽象出来。然后不同的页面,传入一个导航数组即可。

根据我的经验,除了导航数组,可能还会有其他的额外元素,所以在设计时,得留一个参数用来放置除了导航内容之外的东西。

返回的jsx内容大概是长这样的:

<div className="breadcrumb">
  {
    breadcrumbList.map((item, index) => {
      return (
        <span key={index}>
          { index > 0 ? <span className="separator">></span> : null }
          {
            item.router ?
              <a onClick={() => {
                history.push(item.router);
              }}>{ item.name }</a>
              :
              <span>{item.name}</span>
          }
        </span>
      )
    })
  }
  {breadcrumbExtra}
</div>

然后在代码中使用了redux来管理数据。然而我们肯定不希望在每一个页面都充斥着大量的redux数据获取。因此在实际代码中,我写了一个hooks,来实现这个功能:

import { useEffect } from 'react';
import { useActions } from 'easy-peasy';

function useBreadcrub(list, extra) {

  const { setBreadcrumbList, setBreadcrumbExtra } = useActions(actions => {
    const { setBreadcrumbList, setBreadcrumbExtra } = actions.common;
    return {
      setBreadcrumbList,
      setBreadcrumbExtra
    }
  });

  useEffect(() => {
    setBreadcrumbList(list);
    if (extra) {
      setBreadcrumbExtra(extra);
    } else {
      setBreadcrumbExtra(null);
    }
  }, []);

}

export default useBreadcrub;

有了上面提供的hooks,在使用的时候,就超级简单了,只要在每个页面里面:

useBreadcrumb([
  {
    name: "XXX"
  },
  {
    name: "YYYY"
  }
]);

在写上面代码的过程中,碰到一个坑,就是一开始写出来的代码循环了,引起的原因是这样的:

alt

OK,那很简单了,只要把MainContent组件做成一个PureComponent即可。

hooks里面使用memo即可,然而这样的话,当路由改变时,MainContent并不能作出反应了。

hooks并不是万能的

要解决上面的问题,得用shouldComponentUpdate了。

class MainContent extends Component {
  shouldComponentUpdate(nextProps, nextState) {
    // 判断路由是否和上一样相同,不相同就要重新渲染
    if (nextProps.location.pathname !== this.props.location.pathname) {
      return true;
    }
    return false;
  }
  render() {
    return (
      <div className="main-content">
        <Route exact path="/" component={() => <Redirect to={redirectUrl} />}  />
        {
          getRoutes()
        }
      </div>
    )
  }
}

因此,我们能看出hooks并不是万能的,起码它没有提供像classshouldComponentUpdate那么精细的控制。

table高度的处理

需求是:希望table显示在一屏内,如果高度不够的话,固定头滚动。

解决思路是antd提供了scrollY的配置,只要传入该参数即可。

需要强调的是,该值为tbody的高度。

这里我代码的实现,也是依靠了强大的hooks。

// 把该值传到Table的配置中
const tableScrollY = useTableScrollY(tableRef);
// useTableScrollY.js
import { useEffect, useState } from 'react';

const padding = 15 + 20;            // 底边距
const theadHeight = 44;             // 表头高度
const paginationHeight = 30 + 32;   // 分页器高度,32为上下margin之和(margin: 16px 0)

function useTableScrollY(tableRef) {

  const initScrollY = 120;

  const [scrollY, setScrollY] = useState(initScrollY);

  useEffect(() => {
    const tableWrapper = tableRef.current;
    const top = tableWrapper.getBoundingClientRect().top; // 计算table的top值
    function resizeWindow() {
      let calcScrollY = window.innerHeight - top - padding - theadHeight - paginationHeight - 2;
      if (calcScrollY < initScrollY) {
        calcScrollY = initScrollY;
      }
      setScrollY(calcScrollY);
    }
    resizeWindow();
    window.addEventListener('resize', resizeWindow);
    return () => {
      window.removeEventListener('resize', resizeWindow);
    }
  }, []);

  return scrollY;

}

export default useTableScrollY;

form表单

前阵子看到了知乎上的一篇form解决方案:面向复杂场景的高性能表单解决方案(入门篇)

在没出来之前,我本来也想基于antd,搞一个类似的出来的,因为我觉得getFieldDecorator这个单词太难记了,而且到处充斥着这样的代码,着实让我不爽。

自己试用了一下,用到以下几个场景:

  • 普通写法
  • 自定义组件x-component
  • 级联
  • 步骤条(多个form,最后一步提交)

说下几个坑(其实也不算是坑):

  • 像searchForm,是不需要错误信息的div,但那个会占一定的高度,导致css需要重写
  • 在做级联时,A下拉的改变影响B下拉,B下拉要回到placeholder状态,得这样来写:
actions.setFieldState("type", state => {
  state.value = undefined; // 回到之前的placeholder状态
  state.props.enum = [
    {
      label: "类型六",
      value: "4"
    }
  ];
});

步骤条的思路

每一步都做成一个form,都维护一个自己的action。

然后到最后一步时,通过Promise.all所有action.submit()

alt

我们来看一下,单个返回的是什么结果:

action.submit().then((param) => {
  console.log(param);
});

alt

总结

体验了一下,相当不错。建议大家可以加一下钉钉群,有什么问题直接在群里面发问即可。

本文链接:www.my-fe.pub/post/admin-template-2.html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。