chore: update blog (#50069)

* chore: update blog

* chore(dumi): fix code previewer
This commit is contained in:
2024-07-26 03:26:31 +08:00 committed by GitHub
parent 0175a22be0
commit 7120673495
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 159 additions and 63 deletions

View File

@ -39,6 +39,13 @@ export const getHash = (str: string, length = 8) =>
class AntdReactTechStack extends ReactTechStack {
// eslint-disable-next-line class-methods-use-this
generatePreviewerProps(...[props, opts]: any) {
props.pkgDependencyList = { ...devDependencies, ...dependencies };
props.jsx ??= '';
if (opts.type === 'code-block') {
props.jsx = opts?.entryPointCode ? sylvanas.parseText(opts.entryPointCode) : '';
}
if (opts.type === 'external') {
// try to find md file with the same name as the demo tsx file
const locale = opts.mdAbsPath.match(/index\.([a-z-]+)\.md$/i)?.[1];
@ -48,7 +55,6 @@ class AntdReactTechStack extends ReactTechStack {
const codePath = opts.fileAbsPath!.replace(/\.\w+$/, '.tsx');
const code = fs.existsSync(codePath) ? fs.readFileSync(codePath, 'utf-8') : '';
props.pkgDependencyList = { ...devDependencies, ...dependencies };
props.jsx = sylvanas.parseText(code);
if (md) {

View File

@ -75,33 +75,47 @@ rules={[{
## Final Result
```tsx
```tsx | demo
/**
* defaultShowCode: true
*/
import React from 'react';
import type { FormItemProps } from 'antd';
import { Cascader, Form } from 'antd';
import { Button, Cascader, DatePicker, Form as OriginForm } from 'antd';
import dayjs from 'dayjs';
export const AggregateFormItem = (
props: FormItemProps & { names?: FormItemProps<Record<string, any>>['name'][] },
) => {
const form = Form.useFormInstance();
interface AggregateProps<V = any> extends FormItemProps<V> {
names?: FormItemProps<V>['name'][];
}
const Aggregate = (props: AggregateProps) => {
const form = OriginForm.useFormInstance();
const { names = [], rules = [], ...rest } = props;
const [firstName, ...resetNames] = names;
return (
<>
<Form.Item
<OriginForm.Item
name={firstName}
// Convert the values of names into an array passed to children
getValueProps={() => ({ value: names.map((name) => form.getFieldValue(name)) })}
getValueProps={() => {
const value = names.map((name) => form.getFieldValue(name));
if (value.every((v) => v === undefined)) {
return undefined;
}
return { value };
}}
getValueFromEvent={(values) => {
// Set the form store values for names
form.setFields(names.map((name, index) => ({ name, value: values[index] })));
return values[0];
const fieldData = names.map((name, index) => ({ name, value: values?.[index] }));
form.setFields(fieldData);
return values?.[0];
}}
rules={rules.map((thisRule) => {
if (typeof thisRule === 'object') {
rules={rules.map((rule) => {
if (typeof rule === 'object' && rule) {
return {
...thisRule,
...rule,
transform: () => {
// Set the values of the names fields for the rule value
const values = names.map((name) => form.getFieldValue(name));
@ -109,39 +123,70 @@ export const AggregateFormItem = (
},
};
}
return thisRule;
return rule;
})}
{...rest}
/>
{/* Bind other fields so they can getFieldValue to get values and setFields to set values */}
{resetNames.map((name) => (
<Form.Item key={name?.toString()} name={name} noStyle />
<OriginForm.Item key={name?.toString()} name={name} noStyle />
))}
</>
);
};
const data = { province: 'Beijing', city: 'Haidian' };
const options = [
{ value: 'zhejiang', label: 'Zhejiang', children: [{ value: 'hangzhou', label: 'Hangzhou' }] },
{ value: 'jiangsu', label: 'Jiangsu', children: [{ value: 'nanjing', label: 'Nanjing' }] },
];
const createUser = (values) => console.log(values);
const data = {
province: 'Beijing',
city: 'Haidian',
startTime: dayjs(),
endTime: dayjs().add(1, 'month'),
};
export const Demo = () => (
<Form
initialValues={data}
onFinish={(values) => {
createUser(values);
}}
>
<AggregateFormItem label="Address" names={['province', 'city']} rules={[{ required: true }]}>
const options = [
{
value: 'zhejiang',
label: 'Zhejiang',
children: [{ value: 'hangzhou', label: 'Hangzhou' }],
},
{
value: 'jiangsu',
label: 'Jiangsu',
children: [{ value: 'nanjing', label: 'Nanjing' }],
},
];
const Form = Object.assign(OriginForm, { Aggregate });
export default () => (
<Form initialValues={data} onFinish={(value) => console.log(value)}>
<Form.Aggregate label="Address" names={['province', 'city']} rules={[{ required: true }]}>
<Cascader options={options} placeholder="Please select" />
</AggregateFormItem>
</Form.Aggregate>
<Form.Item label="Address (use Default)" name="defaultAddress">
<Cascader options={options} placeholder="Please select" />
</Form.Item>
{/* Similarly, it also applies <DatePicker.RangePicker /> */}
<Form.Aggregate label="Date" names={['startTime', 'endTime']}>
<DatePicker.RangePicker />
</Form.Aggregate>
<Form.Item>
<Button htmlType="submit" type="primary">
Submit
</Button>
</Form.Item>
</Form>
);
```
## Summary
By doing so, we have implemented a feature that allows for operating multiple `names` within a `Form.Item`, making the form logic clearer and easier to maintain. Additionally, there are some edge cases in this example that have not been considered. For instance, `setFields([{ name:'city', value:'nanjing' }])` will not update the selected value of `Cascader`. To achieve a refresh effect, you need to add `Form.useWatch(values => resetNames.map(name => get(values, name)), form);`. Feel free to explore more edge cases and handle them as needed.
By doing so, we have implemented a feature that allows for operating multiple `names` within a `Form.Item`, making the form logic clearer and easier to maintain.
In addition to the `Cascader` example in the text, it is also applicable to components such as `DatePicker.RangePicker`. In other words, this method can be used in any scenario that requires the aggregation of multiple fields.
Additionally, there are some edge cases in this example that have not been considered. For instance, `setFields([{ name:'city', value:'nanjing' }])` will not update the selected value of `Cascader`. To achieve a refresh effect, you need to add `Form.useWatch(values => resetNames.map(name => get(values, name)), form);`.
Feel free to explore more edge cases and handle them as needed.

View File

@ -75,33 +75,47 @@ rules={[{
## 最终效果
```tsx
```tsx | demo
/**
* defaultShowCode: true
*/
import React from 'react';
import type { FormItemProps } from 'antd';
import { Cascader, Form } from 'antd';
import { Button, Cascader, DatePicker, Form as OriginForm } from 'antd';
import dayjs from 'dayjs';
export const AggregateFormItem = (
props: FormItemProps & { names?: FormItemProps<Record<string, any>>['name'][] },
) => {
const form = Form.useFormInstance();
interface AggregateProps<V = any> extends FormItemProps<V> {
names?: FormItemProps<V>['name'][];
}
const Aggregate = (props: AggregateProps) => {
const form = OriginForm.useFormInstance();
const { names = [], rules = [], ...rest } = props;
const [firstName, ...resetNames] = names;
return (
<>
<Form.Item
<OriginForm.Item
name={firstName}
// 将 names 的值转成数组传给 children
getValueProps={() => ({ value: names.map((name) => form.getFieldValue(name)) })}
getValueProps={() => {
const value = names.map((name) => form.getFieldValue(name));
if (value.every((v) => v === undefined)) {
return undefined;
}
return { value };
}}
getValueFromEvent={(values) => {
// 将 form store 分别设置给 names
form.setFields(names.map((name, index) => ({ name, value: values[index] })));
return values[0];
const fieldData = names.map((name, index) => ({ name, value: values?.[index] }));
form.setFields(fieldData);
return values?.[0];
}}
rules={rules.map((thisRule) => {
if (typeof thisRule === 'object') {
rules={rules.map((rule) => {
if (typeof rule === 'object' && rule) {
return {
...thisRule,
...rule,
transform: () => {
// 将 names 字段的值设置给 rule value
const values = names.map((name) => form.getFieldValue(name));
@ -109,39 +123,70 @@ export const AggregateFormItem = (
},
};
}
return thisRule;
return rule;
})}
{...rest}
/>
{/* 绑定其他字段,使其可以 getFieldValue 获取值、setFields 设置值 */}
{resetNames.map((name) => (
<Form.Item key={name?.toString()} name={name} noStyle />
<OriginForm.Item key={name?.toString()} name={name} noStyle />
))}
</>
);
};
const data = { province: 'Beijing', city: 'Haidian' };
const options = [
{ value: 'zhejiang', label: 'Zhejiang', children: [{ value: 'hangzhou', label: 'Hangzhou' }] },
{ value: 'jiangsu', label: 'Jiangsu', children: [{ value: 'nanjing', label: 'Nanjing' }] },
];
const createUser = (values) => console.log(values);
const data = {
province: 'Beijing',
city: 'Haidian',
startTime: dayjs(),
endTime: dayjs().add(1, 'month'),
};
export const Demo = () => (
<Form
initialValues={data}
onFinish={(values) => {
createUser(values);
}}
>
<AggregateFormItem label="Address" names={['province', 'city']} rules={[{ required: true }]}>
const options = [
{
value: 'zhejiang',
label: 'Zhejiang',
children: [{ value: 'hangzhou', label: 'Hangzhou' }],
},
{
value: 'jiangsu',
label: 'Jiangsu',
children: [{ value: 'nanjing', label: 'Nanjing' }],
},
];
const Form = Object.assign(OriginForm, { Aggregate });
export default () => (
<Form initialValues={data} onFinish={(value) => console.log(value)}>
<Form.Aggregate label="Address" names={['province', 'city']} rules={[{ required: true }]}>
<Cascader options={options} placeholder="Please select" />
</AggregateFormItem>
</Form.Aggregate>
<Form.Item label="Address (use Default)" name="defaultAddress">
<Cascader options={options} placeholder="Please select" />
</Form.Item>
{/* 同理,也适用于 DatePicker.RangePicker */}
<Form.Aggregate label="Date" names={['startTime', 'endTime']}>
<DatePicker.RangePicker />
</Form.Aggregate>
<Form.Item>
<Button htmlType="submit" type="primary">
提交
</Button>
</Form.Item>
</Form>
);
```
## 总结
通过这种方式,我们实现了一个可以在 `Form.Item` 中操作多个 `name` 的功能,使得表单逻辑更加清晰和易于维护。另外此示例还有些边界场景没有考虑,比如 `setFields([{ name:'city' value:'nanjing' }])` 不会更新 `Cascader` 选中的值,需要增加 `Form.useWatch(values => resetNames.map(name => get(values, name)), form);` 达到刷新效果等。更多的边界问题就交给你去试试吧~
通过这种方式,我们实现了一个可以在 `Form.Item` 中操作多个 `name` 的功能,使得表单逻辑更加清晰和易于维护。
除了文中的 `Cascader` 示例,同样适用于 `DatePicker.RangePicker` 等组件。换句话说,只要是需要聚合多个字段的场景,都可以使用这种方式。
另外此示例还有些边界场景没有考虑,比如 `setFields([{ name:'city' value:'nanjing' }])` 不会更新 `Cascader` 选中的值,需要增加 `Form.useWatch(values => resetNames.map(name => get(values, name)), form);` 达到刷新效果等。
更多的边界问题就交给你去试试吧~