Most of our development students spend 90% of their time dealing with business, so I have compiled some functions and methods in my work to reduce workload and share them with you! (Continuing to add…)

1. Thousandths of the amount to be processed

Background: since last year in the new unit, begin to enter the financial sector, so demand, dealing with the amount of more and more, demand: in the process of dealing with data, we get that amount is often divided into a string of Numbers, according to the demand, we are going to convert to the unit for ‘yuan’ micrometer data, here are several scenarios out different solutions:

  1. Short version, reserving two decimal places, accepting onenumberTo return tonumber
const handleThousands = (amount: any) => { let num; const value = parseFloat(amount).toFixed(2); If (value. IndexOf (') = = = 1) {num = value. The replace (/ \ d {1, 3} (? =(\d{3})+$)/g, s => `${s},`); } else { num = value.replace(/(\d)(? =(\d{3})+\.) /g, s => `${s},`); } return num; }; /** ** @param amountValue Value to be formatted * @param isCents Default data is in the unit of 'minutes'. If the data is in the unit of' yuan ', */ export const handleAmount = (amountValue = 0, isCents = true): number => { if (amountValue) { const normalAmountValue = isCents ? amountValue / 100 : amountValue; return handleThousands(normalAmountValue); } return 0; };Copy the code

A. In minutes (no need to pass the second parameter by default) :

console.log(handleAmount(788000));      // 7,880.00
Copy the code

B. In yuan (the second argument is given false) :

console.log(handleAmount(788000, false));  // 788,000.00
Copy the code
  1. Upgrade version, product manager has special requirements, according to the situation to retain decimal, add corresponding units, receivenumberTo return tostring
  • Specific requirements:
  • 1, [less than 1 million] use units (yuan), keep 0 decimal places. Example: ¥234,500 yuan
  • 2, [greater than or equal to 1 million, less than 100 million] use the unit (ten thousand), keep 2 decimal places. Example: ¥4,500.25 million
  • 3. [greater than or equal to 100 million] use units (100 million) and keep 4 decimal places. Example: ¥535.41 million
const handleThousands = (amount, val) => { let result, num; let value = parseFloat(amount).toFixed(val); If (value. IndexOf (') = = = 1) {num = value. The replace (/ \ d {1, 3} (? =(\d{3})+$)/g, function(s) { return s + ','; }); } else { num = value.replace(/(\d)(? =(\d{3})+\.) /g, function(s) { return s + ','; }); } if (val === 2) {result = '¥${num} million'; } else if (val === 4) {result = '¥${num} billion'; } else {result = '¥${num} yuan'; } return result; }; /** ** @param {number} amountValue Value to be formatted, * @return {string} with currency units and currency symbols, for example, '¥100 yuan'; */ export const handleAmount = (amountValue: number): string => { if (amountValue) { const oldAmountValue = amountValue; const normalAmountValue = oldAmountValue / 100; if (normalAmountValue > 100000000) { return handleThousands(normalAmountValue / 100000000, 4); } else if (normalAmountValue > 1000000) { return handleThousands(normalAmountValue / 10000, 2); } else { return handleThousands(normalAmountValue, 0); }} else {return '¥0'; }};Copy the code

A. In yuan

console.log(handleAmount(788000)); / / selections of 7880 yuanCopy the code

B. In 10,000 units

console.log(handleAmount(788000000)); 7.88 million / / selectionsCopy the code

C. In hundreds of millions

console.log(handleAmount(788000000000)); 7.88 billion / / selectionsCopy the code

Two, decimal to percentage value

I give two modes according to the usage scenario:

  • Strict mode: down trade-off, keep two decimal places;
  • Non-strict free mode: round, reserved decimal places can be customized
/** * decimal to percentage, defaults to exact measurement (no rounding), and keeps two decimal digits * @param {number} value Small values passed in for example: 0.223455 * @param {Boolean} percent Specifies whether to add the unit '%'. The default value is' % '. False * @param {number} precision round and can set the exact decimal place, default not use; Example: 2 */ export const getDecimalplaces2percent = (value = 0, percent = true, precision = 0,): string | number => { const count = precision ? (value * 100).toFixed(precision) : Math.floor(value * 100 * 100) / 100; if (! percent) { return count; } return `${count}%`; };Copy the code

Call:

A. By default, round to two decimal places

The console. The log (getDecimalplaces2percent (0.234343434)); / / 23.43%Copy the code

B. Pure numbers without percent signs

The console. The log (getDecimalplaces2percent (0.234343434, false)); / / 23.43Copy the code

C. Set the exact decimal place

The console. The log (getDecimalplaces2percent (0.234343434, true, 4)); / / 23.4343%Copy the code

Three, multi-environment configuration

Our project usually has more than one or two environments, so how can we elegantly select different parameters for different environment configurations?

const { ENV } = window; const settingsDict = { local: { adminUrl: 'https://admin-qa.xxxxxxxxx.com', courseUrl: 'https://app-qa.xxxxxxxxx.com', insuranceUrl: 'https://insurance-qa.xxxxxxxxx.com', }, qa: { adminUrl: 'https://admin-qa.xxxxxxxxx.com', courseUrl: 'https://app-qa.xxxxxxxxx.com', insuranceUrl: 'https://insurance-qa.xxxxxxxxx.com', }, staging: { adminUrl: 'https://admin-staging.xxxxxxxxx.com', courseUrl: 'https://app-staging.xxxxxxxxx.com', insuranceUrl: 'https://insurance-staging.xxxxxxxxx.com', }, production: { adminUrl: 'https://admin.xxxxxxxxx.com', courseUrl: 'https://app.xxxxxxxxx.com', insuranceUrl: 'https://i.xxxxxxxxx.com', }, }; const config = settingsDict[ENV] || settingsDict.local; const settings = { ... config, getAdminUrl: () => config.adminUrl, getInsuranceUrl: (pathname) =>`config.insuranceUrl${pathname}`, }; export default settings;Copy the code

Write the corresponding configuration into a configuration file, by taking the current environment, return the corresponding URL, also can pass the required parameters, splicing into the desired address.

Method call:

settings.getInsuranceUrl('detail')   => https://insurance-qa.xxxxxxxxx.com/detail

Copy the code

4. Optimization of multi-interval condition matching

Multi-interval logic judgment: integrate each condition and returned message in the way of an object, and then select it in a circular way.

export const getGreetingMessage = name => { const hour = new Date().getHours(); Const messages = [{check: () => hour < 9 && Hour >= 5, format: () => 'good morning, ${name},',}, {check: && () = > hour < 12 hour > = 9, format: () = > good morning `, ${name}, start a new day work! `,}, {check: () => hour < 14&& hour >= 12, format: () => '${name},',}, {check: () = > hour < 19 && hour > = 14, format: () = > ` good afternoon, ${name}, focus on goals. `,}, {check: () = > hour < 23 && hour > = 19, format: () = > ` good evening, ${name}, and to target a step forward! `,}, {check: () = > hour > = 23, the format: () => 'Go to bed early ',},]; Let message = 'hello '; messages.forEach(item => { if (item.check()) { message = item.format(name); }}); return message; };Copy the code

5. Add width and height to IMG

Concatenate width and height to img URL address

interface IImageResponse { width: number; height: number; url: string; } /** * handle img, @param {string[]} imgArr accepts an img array containing image links * @return {IImageResponse[]} returns an array of objects with width, height, url links */ export const  handleImgInfos = (imgArr: string[]): IImageResponse[] => { const images = []; // create img imgarr.foreach (item => {const image = new image (); image.src = item; image.onload = () => { const { width, height } = image; images.push({ width, height, url: item }); }; }); return images; };Copy the code

Call method:

/ / into the array of ginseng contain image links const imgStr = 'http://f3.v.veimg.cn/meadincms/1/2015/1123/20151123081509321.jpg' console. The log ( handleImgInfos(['http://f3.v.veimg.cn/meadincms/1/2015/1123/20151123081509321.jpg']) ); // [{width: 500, height: 280, url: "http://f3.v.veimg.cn/meadincms/1/2015/1123/20151123081509321.jpg"}]Copy the code

6. Solutions for dynamically adding and subtracting form items

In normal forms, each item is bound with a key. When we need to dynamically add or subtract form items, we need to deal with them separately. Here is the scene in the process of form processing.

  • External isolation: Use form forms to achieve isolation by creating multiple child form forms
  • Internal relay: by binding different keys to achieve a different id for each item binding to distinguish

Basic scenario:

1. External isolation

The method of external isolation is to create multiple child forms to realize the isolation of child forms and parent forms. In the final submission step, the formList of child component forms and the form of parent component are obtained

On code — — — — — — — — — >

The parent component:

import React, { useState, useCallback } from 'react'; import useForm from 'rc-form-hooks'; import { Form, Input, Button, Divider } from 'antd'; import { v4 as uuidv4 } from 'uuid'; import CardLayout from '@xb/layouts/CardLayout'; import Form1 from './widgets/Form1'; export const formLayout = { labelCol: { span: 5 }, wrapperCol: { span: 15 }, }; const TestForm = () => { const form = useForm(); const { getFieldDecorator, validateFields } = form; const [formList, setFormList] = useState([]); const [msgList, setMsgList] = useState([{ id: uuidv4() }]); // Subcomponent adds const getForm = useCallback(item => {setFormList(r => [...r, item]); }, [formList], ); // Add const onAdd = useCallback(() => {setMsgList([...msgList, {id: uuidv4()}]); }, [msgList]); // delete const onDel = useCallback(I => {// page UI delete const TMP = json.parse (json.stringify (msgList)); tmp.splice(i, 1); setMsgList([...tmp]); // formList delete formlist.splice (I, 1); setFormList(formList); }, [msgList, formList], ); // Submit save const handleSubmit = () => {promise.all ([... formlist.map (item => item.validateFields())), ValidateFields ()]).then(data => {console.log(data, 'default return parameter '); const list = data.slice(0, formList.length); console.log({ ... Data [data. length-1], list}, 'parameter after concatenation '); }); }; return ( <CardLayout> <Form {... FormLayout}> < form. Item label=" name "> {getFieldDecorator('name', {rules: [{required: true, message: 'please enter the name of'}],}) (< Input / >)} < / Form Item > < Form. The Item label = "type" > {getFieldDecorator (' type '{rules: [{required: true, message: 'please enter type,},],}) (< Input / >)} < / Form Item > < Button onClick = {onAdd} > add < / Button > {msgList. The map ((Item, index) => { return ( <Form1 key={item.id} msgIndex={index} value={item} onDel={() => { onDel(index); }} getForm={getForm} /> ); })} <Divider /> <Divider /> <Form.Item style={{ marginTop: 30, marginLeft: <Button type="primary" block onClick={handleSubmit}> query </Button> </ form. Item> </Form> </CardLayout>); }; export default TestForm;Copy the code

Child components:

import React, { useEffect } from 'react'; import useForm from 'rc-form-hooks'; import { Form, Input, Row, Col, Icon } from 'antd'; interface IProps { msgIndex: number; value: any; onDel: () => void; getForm: (f) => void; } const formLayout = { labelCol: { span: 6 }, wrapperCol: { span: 17 }, }; const Form1: React.FC<IProps> = props => { const { msgIndex, onDel } = props; const form = useForm(); const { getFieldDecorator } = form; useEffect(() => { props.getForm(form); } []); return ( <Form {... FormLayout}> <Row> <Col span={23}> < form. Item label={'strategy ${msgIndex + 1} '}> {getFieldDecorator('strategy', {rules: [{required: true, the message: 'please enter strategy,},],}) (< Input / >)} < / Form Item > < / Col > < Col span = {1} > {msgIndex! == 0 && ( <Icon type="minus-circle" style={{ fontSize: 16, marginTop: 10, marginLeft: -14 }} onClick={onDel} /> )} </Col> </Row> </Form> ); }; export default Form1;Copy the code

Submission Form:

Parameter acquisition after submission:

2. Internal relay

The internal relay method is to distinguish different items by binding different keys. Multiple forms will not be created, and the final submission will still be the same form. The disadvantage is that each item needs to be split according to the rules

On code — — — — — — — — — >

import React, { useCallback } from 'react'; import useForm from 'rc-form-hooks'; import { Form, Input, Button, Divider, Row, Col, Icon } from 'antd'; import { v4 as uuidv4 } from 'uuid'; import CardLayout from '@xb/layouts/CardLayout'; export const formLayout = { labelCol: { span: 5 }, wrapperCol: { span: 15 }, }; const TestForm = () => { const form = useForm(); const { getFieldDecorator, validateFields, getFieldValue, setFieldsValue } = form; getFieldDecorator('strategy', { initialValue: [{ id: uuidv4() }] }); const strategy = getFieldValue('strategy'); // Add const onAddMsg = useCallback(() => {const strategyList = getFieldValue('strategy'); const nextKeys = [...strategyList, { id: uuidv4() }]; setFieldsValue({ strategy: nextKeys, }); }, [getFieldValue, setFieldsValue]); // Delete const onDel = useCallback(I => {const strategyList = getFieldValue('strategy'); strategyList.splice(i, 1); setFieldsValue({ strategy: strategyList, }); }, [getFieldValue, setFieldsValue], ); // Form submission const handleSubmit = () => {validateFields().then(data => {console.log(data, 'returns parameters by default '); let list = []; const { name, type, strategy, ... strategyList } = data; strategy.forEach(item => { list.push({ strategy: strategyList[`strategy[${item.id}]`] }); }); Console. log({name, type, list}, 'concatenated parameters '); }); }; return ( <CardLayout> <Form {... FormLayout}> < form. Item label=" name "> {getFieldDecorator('name', {rules: [{required: true, message: 'please enter the name of'}],}) (< Input / >)} < / Form Item > < Form. The Item label = "type" > {getFieldDecorator (' type '{rules: [{required: true, message: 'please enter type,},],}) (< Input / >)} < / Form Item > < Button onClick = {onAddMsg} > add < / Button > < Divider / > {strategy. The map ((k, Index) => {return (<Row> <Col span={23}> < form. Item label={' strategy ${index + 1} '}> {getFieldDecorator(' strategy[${K. id}] ', {rules: [{required: true, the message: 'please enter strategy,},],}) (< Input / >)} < / Form Item > < / Col > < Col span = {1} > {index! == 0 && ( <Icon type="minus-circle" style={{ fontSize: 16, marginTop: 10, marginLeft: -14 }} onClick={onDel} /> )} </Col> </Row> ); })} <Form.Item style={{ marginTop: 30, marginLeft: <Button type="primary" block onClick={handleSubmit}> query </Button> </ form. Item> </Form> </CardLayout>); }; export default TestForm;Copy the code

Submission Form:

Parameter acquisition after submission:

Upgrade scenario:

After the realization of the above scenario, the product put forward new requirements and added a new layer to the original dynamic form to realize the dynamic increase and decrease of the group form, as shown in the following figure:

First of all, we have talked about the single-layer dynamic add/subtract form. Now we will focus on how to dynamically add/subtract a nested form.

The outermost component index.tsx

import React, { useState, useCallback } from 'react'; import useForm from 'rc-form-hooks'; import { Form, Input, Button, Divider } from 'antd'; import { v4 as uuidv4 } from 'uuid'; import CardLayout from '@xb/layouts/CardLayout'; import Form1 from './widgets/Form1'; import Form2 from './widgets/Form2'; export const formLayout = { labelCol: { span: 5 }, wrapperCol: { span: 15 }, }; const TestForm = () => { const form = useForm(); const { getFieldDecorator, validateFields } = form; const [formList, setFormList] = useState([]); const [groupFormList, setGroupFormList] = useState([]); const [msgList, setMsgList] = useState([{ id: uuidv4() }]); const [msgGroupList, setGroupMsgList] = useState([{ id: uuidv4() }]); const [subFormList, setSubFormList] = useState([{ id: uuidv4(), form: [] }]); // Add formList const getForm = useCallback(item => {setFormList(r => [...r, item]); }, [formList], ); // Add groupFormList const getGroupForm = useCallback(item => {setGroupFormList(r => [...r, item]); } []); // Add a single const onAdd = useCallback(() => {setMsgList([...msgList, {id: uuidV4 ()}]); }, [msgList]); // Add group const onAddGroup = useCallback(() => {const id = uuidV4 (); setGroupMsgList([...msgGroupList, { id }]); setSubFormList([...subFormList, { id, form: [] }]); }, [msgGroupList, subFormList]); // delete const onDel = useCallback(I => {// page UI delete const TMP = json.parse (json.stringify (msgList)); tmp.splice(i, 1); setMsgList([...tmp]); // formList delete formlist.splice (I, 1); setFormList(formList); }, [msgList, formList], ); Parse (json.stringify (msgGroupList))); // Delete group const onDelGroup = (I: number) => {// page UI delete const TMP = json.parse (json.stringify (msgGroupList)); tmp.splice(i, 1); setGroupMsgList([...tmp]); Groupformlist.splice (I, 1); subFormList.splice(i, 1); setGroupFormList(groupFormList); setSubFormList(subFormList); }; const handleSubmit = () => { const list = subFormList.map(item => item.form); Promise.all([ ...formList.map(item => item.validateFields()), ...groupFormList.map(item => item.validateFields()), List.flat().map(item => item.validateFields()), validateFields(),]).then(data => {console.log(data, 'result '); const singleData = data.slice(0, formList.length); const groupData = [...data.slice(formList.length, groupFormList.length + formList.length)]; console.log(singleData, groupData); groupFormList.map((item, index) => { Promise.all([...list[index].map(i => i.validateFields())]).then(value => { groupData[index]['list'] = value;  }); }); const params = { ... data[data.length - 1], list: singleData, groupData, }; Console. log(params, 'parameters '); }); }; return ( <CardLayout> <Form {... FormLayout}> < form. Item label=" name "> {getFieldDecorator('name', {rules: [{required: true, message: 'please enter the name of'}],}) (< Input / >)} < / Form Item > < Form. The Item label = "type" > {getFieldDecorator (' type '{rules: [{required: true, message: 'Please enter type ',},], }}) (< Input / >) < / Form Item > < Button onClick = {onAdd} > add < / Button > < Divider / > {/ * single strategy * /} {msgList. The map ((Item, index) => { return ( <Form1 key={item.id} msgIndex={index} onDel={() => { onDel(index); }} getForm={getForm} /> ); })} <Divider /> <Button onClick={onAddGroup}> Add group </Button> {/* double strategy */} {msggrouplist.map ((item, index) => { return ( <Form2 key={item.id} msgIndex={index} onDelGroup={() => { onDelGroup(index); }} getGroupForm={getGroupForm} subFormList={subFormList} setSubFormList={setSubFormList} /> ); })} <Divider /> <Form.Item style={{ marginTop: 30, marginLeft: <Button type="primary" block onClick={handleSubmit}> query </Button> </ form. Item> </Form> </CardLayout>); }; export default TestForm;Copy the code

Single-layer policy component form1.tsx:

import React, { useEffect } from 'react'; import useForm from 'rc-form-hooks'; import { Form, Input, Row, Col, Icon } from 'antd'; import { FormMethods } from 'rc-form-hooks/dist/formHooks'; interface IProps { msgIndex: number; onDel: () => void; getForm: (f: FormMethods<any>) => void; } const formLayout = { labelCol: { span: 6 }, wrapperCol: { span: 17 }, }; const Form1: React.FC<IProps> = props => { const { msgIndex, onDel } = props; const form = useForm(); const { getFieldDecorator } = form; useEffect(() => { props.getForm(form); } []); return ( <Form {... FormLayout}> <Row> <Col span={23}> < form. Item label={'strategy ${msgIndex + 1} '}> {getFieldDecorator('strategy', {rules: [{required: true, the message: 'please enter strategy,},],}) (< Input / >)} < / Form Item > < / Col > < Col span = {1} > {msgIndex! == 0 && ( <Icon type="minus-circle" style={{ fontSize: 16, marginTop: 10, marginLeft: -14 }} onClick={onDel} /> )} </Col> </Row> </Form> ); }; export default Form1;Copy the code

Two-layer policy component Form2->index.tsx

import React, { useEffect, useState, useCallback } from 'react'; import useForm from 'rc-form-hooks'; import { Form, Input, Button } from 'antd'; import { v4 as uuidv4 } from 'uuid'; import { FormMethods } from 'rc-form-hooks/dist/formHooks'; import SubPage from './SubPage'; interface IProps { msgIndex: number; onDelGroup: () => void; getGroupForm: (f: FormMethods<any>) => void; subFormList: any; setSubFormList: any; } const formLayout = { labelCol: { span: 5 }, wrapperCol: { span: 15 }, }; const Form2: React.FC<IProps> = props => { const { msgIndex, onDelGroup, subFormList, setSubFormList } = props; const form = useForm(); const { getFieldDecorator } = form; const [msgList, setMsgList] = useState([]); const [formList, setFormList] = useState([]); // Add formList const getForm = useCallback((item, groupIndex) => {const list = subFormList.slice(); setFormList([...formList, item]); Console. log(list, 'Add group subpolicy '); List [groupIndex]. Form = [...list[groupIndex]. Form, item]; setSubFormList(list); }, [formList, subFormList], ); // Add a single const onAdd = useCallback(() => {const id = uuidV4 (); setMsgList([...msgList, { id }]); }, [msgList]); // delete const onDel = useCallback(I => {// page UI delete const TMP = json.parse (json.stringify (msgList)); tmp.splice(i, 1); setMsgList([...tmp]); // Delete formlist.splice (I, 1); setFormList(formList); }, [msgList, formList], ); useEffect(() => { props.getGroupForm(form); } []); return ( <Form {... formLayout}> <div style={{ height: '100%', width: '100%', border: '1px solid pink', marginTop: 20, paddingLeft: {msgIndex + 1} < form. Item label=" type "> {getFieldDecorator('type', {rules: [{required: true, message: 'please enter type,},],}) (< Input / >)} < / Form Item > < Button onClick = {onAdd} > add < / Button > {msgIndex! == 0 && <Button onClick={onDelGroup}> </Button>} {msglist.map ((item, index) => { return ( <SubPage key={item.id} msgIndex={index} groupIndex={msgIndex} onDel={() => { onDel(index); }} getForm={getForm} /> ); })} </div> </Form> ); }; export default Form2;Copy the code

Double nested child component Form-> subPage.tsx

Key: How does a double-layer nested inner form bind to an outer layer and return a form list

import React, { useEffect } from 'react'; import useForm from 'rc-form-hooks'; import { Form, Input, Row, Col, Icon } from 'antd'; import { FormMethods } from 'rc-form-hooks/dist/formHooks'; interface IProps { msgIndex: number; groupIndex: number; onDel: (i: number) => void; getForm: (f: FormMethods<any>, id: number) => void; } const formLayout = { labelCol: { span: 5 }, wrapperCol: { span: 16 }, }; const SubPage: React.FC<IProps> = props => { const { msgIndex, groupIndex, onDel } = props; const form = useForm(); const { getFieldDecorator } = form; useEffect(() => { props.getForm(form, groupIndex); }, [groupIndex]); return ( <Form {... FormLayout}> <Row> <Col span={23}> < form. Item label={'strategy ${msgIndex + 1} '}> {getFieldDecorator('strategy', {rules: [{required: true, the message: 'please enter strategy,},],}) (< Input / >)} < / Form Item > < / Col > < Col span = {1} > {msgIndex! == 0 && ( <Icon type="minus-circle" style={{ fontSize: 16, marginTop: 10, marginLeft: -50 }} onClick={() => { onDel(msgIndex); }} /> )} </Col> </Row> </Form> ); }; export default SubPage;Copy the code