Làm thế nào để Ant Design Pro nương tựa vào Next.js

Đường đường nhà trai là Framework React xịn top 1 server, nhà gái cũng là UI Kit nhiều ⭐️ nhất Github, gia thế không có gì ngoài bộ Components cực khủng. Ấy vậy mà 2 anh em này không nương tựa nhau lắm.

🌿 Tích hợp Antd lên Next.js

Việc cài đặt lúc đầu cũng chưa có gì khó, chỉ cần gõ yarn add antd và thêm dòng

// trong file _app.tsx

import "antd/dist/antd.css";

OK, thế là tạm lên. Nhưng…

🤔 Giờ sửa màu primary thế nào?
🤔 Ủa sao sửa tí mà phải lằng nhà lằng nhằng trong webpack config vậy?
🤔 Ủa sao Ant Design Pro không có hướng dẫn cho SSR như Next.js vậy?
🤔 Ủa sao lỗi tùm lum vậy?
……….

➡️ Next.js hỗ trợ SASS, SCSS nhưng không hỗ trợ LESS - thứ mà Ant Design đang dùng trong mọi ngóc ngách. Trước đây, bản Next.js 9 cũng hõ trợ đấy nhưng chả rõ vì sao sau bỏ luôn.

➡️ Bản thân Ant Design từ đầu đã có khung “toolchain” khác người rồi. Ant Design sử dụng UmiJS (hàng nhà làm) để navigate, quản lí assets, hot reload, build bundle,…

next-plugin-antd-less

✅ Bước đơn giản đầu tiên để giải quyết mọi vấn đề nhức đầu là dùng package next-plugin-antd-less.

yarn add next-plugin-antd-less

Các bác mở next.config.js, copy & paste vào.

const withAntdLess = require('next-plugin-antd-less');

module.exports = withAntdLess({
  modifyVars: { '@primary-color': '#04f' }, // neu khong luu ra file
  lessVarsFilePath: './src/styles/variables.less', // neu luu ra file rieng 
  lessVarsFilePathAppendToEndOfContent: false,
  cssLoaderOptions: {
    // ... 
    mode: "local",
    localIdentName: __DEV__ ? "[local]--[hash:base64:4]" : "[hash:base64:8]",
    exportLocalsConvention: "camelCase",
    exportOnlyLocals: false,
    // ...
    getLocalIdent: (context, localIdentName, localName, options) => {
      return "whatever_random_class_name";
    },
  },

  nextjs: {
    localIdentNameFollowDev: true, 
  },
  webpack(config) {
    return config;
  },
});

Trong config kia sẽ có đoạn localIdentName getLocalIdent có thể gây khó hiểu với 1 vài bác. Thật ra vấn đề là do các class CSS sẽ được tự xử lí thành những đoạn mã hashing, mà lúc đấy thì debug nó thuộc component nào chỉ có bằng nhang khói. Cho nên, họ mới đề xuất mình lắp cái tên component gốc vào trước classname để dễ debug trên Dev.

🌿 Tích hợp Antd Pro

Ant Design có một bộ components nữa gọi là Pro Components. Các components này được xây dựng theo “convention” cho 1 resource CRUD bất kì. Các bác có thể tham khảo template chung của 1 app dùng Ant Design Pro: Preview

Và để bắt đầu pro hơn, các bác gõ lệnh sau:

yarn add @ant-design/pro-form @ant-design/pro-layout \
@ant-design/pro-list @ant-design/pro-skeleton \
@ant-design/pro-table @ant-design/pro-provider

🆘 Ét ô ét: Cannot use import statement outside a module

Ngay khi các bác có ý định import {LoginForm} from '@ant-design/pro-form' thì lỗi trên sẽ xuất hiện. Vấn đề nó xảy ra ở trong thư viện, họ require một file CSS khi import component này. Mà như các bác đã biết, Next.js không cho phép import 1 file CSS trong component mà phải import từ tầng cao nhất (_app)

Hành trình đi tìm cách workaround 🙂

Đầu tiên cháu thử sửa kiểu thêm dynamic, vì nó có thể chặn Next render component này ở Server Side

OK, lên. Nhưng vấn đề là cháu import được ProForm, mà cái cháu đang cần import là LoginForm cơ.

Export kiểu này ai chơi. export default ở đây là ProForm, và nó cũng được export cùng với những bọn còn lại (named export). Hay thử import thẳng từ code như dưới nhỉ?

Họ lại dùng LoadableComponent làm vỏ (để Lazy Load) khi export component này, dẫn đến import thẳng từ code sẽ không thể sử dụng được

✅ Thật ra cách import đúng sẽ như thế này

const LoginForm = dynamic(
  () => import("@ant-design/pro-form").then((module: any) => module.LoginForm),
  {
    ssr: false,
  }
);

Sau khi import các bác cần chỉ định module sẽ return trong trường hợp có nhiều đối tượng cùng export. Và kết quả thành công

Các bác sẽ phải viết như vậy với từng component, kể cả các component nếu họ viết dạng sub module như ProText.Password

Thật ra cháu còn một vấn đề nhức nhối nữa là locale chưa thể set được, dẫn đến các nút submit mà họ dùng i18n đều là tiếng trung. Sau một hồi nghiên cứu cách họ export Context, Provider cháu quyết định: