Simple String Selector with Ant Design Mobile
Introduction
Ant Design Mobile provides a Picker
component to select a value from a list. However, it is not easy to use it to select a string value.
In this post, I will create a SimpleStringSelector
component with the Picker
. It provides a simple way to select a string value from a list, it can fetch options asynchronously, and it is a Form.Item
by default.
You can use it like this:
import React from 'react';
import { Form } from 'antd-mobile';
const Demo = () => {
const [form] = Form.useForm();
return (
<Form form={form}>
<SimpleStringSelector
form={form}
label="Name"
formName="name"
placeholder="Please select name"
rules={[{ required: true, message: 'Please select name' }]}
fetchOptions={async () => {
// simulate a network request
return ['name1', 'name2'];
}}
/>
</Form>
);
};
Code
import React from 'react';
import {
Form,
Picker,
} from 'antd-mobile';
type SimpleStringSelectorProps = {
label: string;
formName: string;
placeholder?: string;
rules?: Rule[];
initialValue?: string;
/**
* fetch data asynchronously
*/
fetchOptions: () => Promise<string[]>;
/**
* form instance, it is used to reload initial value when changed
*/
form?: FormInstance;
};
const SimpleStringSelector = ({
label,
formName,
placeholder,
rules,
initialValue,
fetchOptions,
form,
}: SimpleStringSelectorProps) => {
const [loading, setLoading] = useState(false);
const [options, setOptions] = useState<{ label: string; value: string }[]>([]);
const [visible, setVisible] = useState(false);
const [value, setValue] = useState<string[] | undefined>()
useEffect(() => {
const _fetchOptions = async () => {
if (loading) {
return;
}
setLoading(true);
try {
const data = await fetchOptions();
const options = data.map((option) => ({
value: option,
label: option,
}));
setOptions(options);
} catch (e) {
console.error(e);
}
setLoading(false);
};
_fetchOptions().then();
}, [0]);
// update initial value when it is changed
useEffect(() => {
if (initialValue) {
form?.setFieldsValue({[formName]: value})
}
}, [initialValue]);
// prevent rendering while loading
if (loading) {
return <></>;
}
return (
<Form.Item
name={formName}
trigger='onConfirm'
label={label}
rules={rules}
initialValue={initialValue ? [initialValue] : []}
onClick={() => setVisible(true)}
>
<Picker
columns={[options]}
visible={visible}
onClose={() => setVisible(false)}
value={value}
onConfirm={(v) => {
setValue(v as string[]);
}}
>
{(value) => {
return value[0]?.value || placeholder || `Please select ${label};
}}
</Picker>
</Form.Item>
);
};