Jinyang's Blog

website·Indexed 30 days ago

https://jinyang79.github.io/

A collection of front-end development records and shares by Jinyang, covering topics such as React, Redux Toolkit, Vite migration, Ant Design 4.x upgrades, SignalR, SheetJS, and low-code platform practices.

Tokens
7.7K
Snippets
40
Records
46
Agent score
75%

What's inside Jinyang's Blog

  1. Overview of the Low-code H5 Activity Page Platform

    The Low-code H5 Activity Page Platform is a visual drag-and-drop development environment designed specifically for building H5 activity pages. It allows developers to quickly construct pages and generate standalone HTML files for direct use, while still supporting secondary development to handle complex business logic. The platform is built with Vue and supports multiple targets including H5, Apps, and Mini-programs.
  2. Reduce repetitive state updates using addMatcher in Redux Toolkit

    When multiple async thunks share the same state transitions (e.g., setting loading = true on any pending action), use builder.addMatcher instead of repeating builder.addCase for every action.

    addMatcher takes a predicate function that returns a boolean. If the function returns true for a given action, the reducer logic is executed.

    Critical Constraint: All builder.addMatcher calls must be placed after all builder.addCase calls in the extraReducers builder.

    import { AnyAction, createSlice } from '@reduxjs/toolkit';
    
    // 1. Define matcher functions to identify action phases
    export const isPendingAction = (action: AnyAction) => action.type.endsWith('/pending');
    export const isFulfilledAction = (action: AnyAction) => action.type.endsWith('/fulfilled');
    export const isRejectedAction = (action: AnyAction) => action.type.endsWith('/rejected');
    
    export const todosSlice = createSlice({
      name: 'todos',
      initialState,
      reducers: {},
      extraReducers: (builder) => {
        // Handle specific success logic first
        builder
          .addCase(getTodoList.fulfilled, (state, action) => { /* success logic */ })
          .addCase(postTodoItem.fulfilled, (state, action) => { /* success logic */ });
    
        // Use matchers for shared loading states at the end
        builder
          .addMatcher(isPendingAction, (state) => {
            state.loading = true;
          })
          .addMatcher(
            (action) => isFulfilledAction(action) || isRejectedAction(action),
            (state) => {
              state.loading = false;
            }
          );
      },
    });
  3. Wake a Mac remotely using the Find My app

    To wake a Mac that is in 'Sleep + Wi-Fi' mode with a remote control app (e.g., ToDesk) running in the background, you can use the Apple 'Find My' app on an iPhone. Opening the 'Find My' app and waiting for the device status to refresh to 'Location: Now' triggers the Mac to wake, allowing the remote control software to appear online and be accessible.
    1. Open 'Find My' app on iPhone -> Wait for status to update to 'Location: Now'.
    2. Open ToDesk (or similar remote app) -> Connect to the now-online Mac.
  4. Import XLSX files to JSON using SheetJS

    To import an Excel file, use the FileReader API to read the file as a binary string. Then, use XLSX.read to parse the workbook and XLSX.utils.sheet_to_json to convert each sheet's content into a JavaScript object array where the Excel headers serve as the keys.
    import XLSX from 'xlsx';
    import { message } from 'antd';
    
    interface XlsxToJsonProps {
      file: File;
      onSuccess: (data: Record<string, string>[]) => void;
    }
    
    export function xlsxToJson({ file, onSuccess }: XlsxToJsonProps): void {
      let data: Record<string, string>[] = [];
      const fileReader = new FileReader();
      fileReader.readAsBinaryString(file);
    
      fileReader.onload = (event: ProgressEvent<FileReader>) => {
        try {
          const result = event.target?.result;
          const workbook = XLSX.read(result, { type: 'binary' });
    
          for (const sheet in workbook.Sheets) {
            if (Object.prototype.hasOwnProperty.call(workbook.Sheets, sheet)) {
              data = data.concat(XLSX.utils.sheet_to_json(workbook.Sheets[sheet]));
            }
          }
          onSuccess(data);
        } catch (error) {
          message.error(`Import failed: ${error}`);
        }
      };
    }
  5. Migrate Legacy Icons to Ant Design v4 Icons

    The upgrade tool converts <Icon type={type} /> to <LegacyIcon type={type} /> from @ant-design/compatible. This causes the entire SVG icon library to be bundled, significantly increasing bundle size. To optimize, manually migrate to the new按需 (on-demand) import method using @ant-design/icons.
    // AVOID THIS (increases bundle size)
    import { Icon as LegacyIcon } from '@ant-design/compatible';
    <LegacyIcon type="laptop" />
    
    // DO THIS (on-demand import)
    import { LaptopOutlined } from '@ant-design/icons';
    <LaptopOutlined />
  6. Export JSON data to XLSX using SheetJS

    To export JSON data to an Excel file, use a mapping array to translate technical JSON keys into human-readable table headers. The process involves converting the JSON array into a worksheet using XLSX.utils.json_to_sheet, creating a new workbook, and triggering a file download via XLSX.writeFile.
    import { message } from 'antd';
    import XLSX from 'xlsx';
    
    interface JsonToXlsxProps {
      res: Record<string, any>[]; // API response data
      fileName: string;           // Filename and sheet name
      mapKeyToHeader: [string, string][]; // Mapping of [jsonKey, headerName]
    }
    
    export function jsonToXlsx({ res, fileName, mapKeyToHeader }: JsonToXlsxProps): void {
      const headerMap = new Map<string, string>(mapKeyToHeader);
    
      const exportData = res.map((item) => {
        const dataItem: Record<string, string> = {};
        headerMap.forEach((value, key) => {
          dataItem[value] = item[key];
        });
        return dataItem;
      });
    
      try {
        const worksheet = XLSX.utils.json_to_sheet(exportData);
        const workbook = XLSX.utils.book_new();
        XLSX.utils.book_append_sheet(workbook, worksheet, fileName);
        XLSX.writeFile(workbook, `${fileName}.xlsx`);
      } catch (error) {
        message.error(`Export failed: ${error}`);
      }
    }
    
    // Usage Example:
    jsonToXlsx({
      res: result,
      fileName: 'EmployeeInfo',
      mapKeyToHeader: [
        ['name', 'Name'],
        ['age', 'Age'],
        ['address', 'Address'],
      ],
    });
  7. Apply JavaScript variable and function naming conventions

    Follow these naming rules to improve code readability and maintainability:

    1. Avoid abbreviations: Do not shorten words unless they are industry-standard (e.g., avoid ass for Association or sb for StringBuilder).
    2. Variables: Use nouns or noun phrases (e.g., value, options, fileText, columnName).
    3. Booleans:
      • Use is... for "is it..."
      • Use has... for "does it have..."
      • Use can... for "can it..."
      • Use ...able for "is it able to..."
    4. Functions: Use a verb-object pattern (e.g., getUserInfo, insertRows, clearValue).
    5. Avoid generic/temporary names: Do not use _ prefixes, temp, or my. Every variable should have a meaningful name.
    6. Be specific: Avoid vague names like info or count; use userInfo or clickCount instead.
  8. Handle environment variables in Vite

    Vite does not use process.env. Instead, use the import.meta.env object. Only variables prefixed with VITE_ are exposed to the client-side code to prevent leaking sensitive information.
    // Replace this:
    // const env = process.env.REACT_APP_BUILD_ENV
    
    // With this:
    const env = import.meta.env.VITE_APP_ENV
    // package.json
    "scripts": {
        "start": "cross-env VITE_APP_ENV=local vite"
    }
  9. Migrate a React project from Webpack to Vite

    To migrate a React project from Webpack to Vite, follow these steps:

    1. Remove Webpack artifacts: Delete the config and scripts directories (if the project was ejected) and uninstall all webpack-related dependencies (e.g., webpack, xxx-loader, xxx-webpack-plugin, babel-xxx, postcss-xxx).
    2. Install Vite: Add vite and @vitejs/plugin-react-refresh as devDependencies.
    3. Configure Vite: Create a vite.config.js file and set up the React refresh plugin.
    4. Update package.json: Update scripts to use vite for starting, building, and previewing.
    5. Relocate index.html: Move public/index.html to the project root. Update the HTML to include a module script pointing to your entry point (e.g., /src/index.jsx). Move other static assets to src/assets/ and remove the public directory.
    6. Start the server: Run the start script to begin the development server.
    yarn add vite @vitejs/plugin-react-refresh -D
    // vite.config.js
    import { defineConfig } from "vite";
    import reactRefresh from "@vitejs/plugin-react-refresh";
    
    export default defineConfig({
      plugins: [
        reactRefresh(),
      ],
    });
    // package.json
    {
      "scripts": {
        "start": "vite",
        "build": "vite build",
        "preview": "vite preview"
      }
    }
    <!-- index.html -->
    <!DOCTYPE html>
    <html lang="zh-CN">
      <body>
        <div id="root"></div>
        <script type="module" src="/src/index.jsx"></script>
      </body>
    </html>
  10. Pass function arguments as objects for better scalability

    When a function requires more than two parameters, pass them as a single object instead of a parameter list. This improves readability, makes the order of arguments irrelevant, and simplifies the handling of optional parameters.
    // Avoid long parameter lists
    // function getUserInfo(name, age, sex, Address, phone) { ... }
    
    // Use an object for arguments
    function getUserInfo(args) {
      const { name, age, sex, Address, phone } = args;
      // ...
    }
    
    getUserInfo({
      phone: '1388888',
      sex: 'male',
      age: 10,
      name: 'John',
    });
  11. Simplify conditional checks using logical operators and arrays

    You can reduce the verbosity of your code by using logical AND (&&) for simple conditional execution and Array.prototype.includes() for checking a value against multiple possibilities.
    // Use && instead of if
    // if (a > 10) { doSomething(a); }
    a > 10 && doSomething(a);
    
    // Use .includes() instead of multiple OR checks
    // if (a === undefined || a === 10 || a === 15 || a === null)
    if ([undefined, 10, 15, null].includes(a)) {
      // ...
    }
  12. Automate Ant Design v3 to v4 upgrade using codemod

    Use the official @ant-design/codemod-v4 CLI tool to automatically migrate your project to Ant Design v4. The tool handles dependency updates and basic code transformations.
    # Run directly via npx
    npx -p @ant-design/codemod-v4 antd4-codemod src
    
    # OR install globally and run
    npm i -g @ant-design/codemod-v4
    # or
    yarn global add @ant-design/codemod-v4
    
    antd4-codemod src