打字稿是控制参数的正确类型?
Typescript which is correct type for control parameter?
我为我的表单创建了一个打字稿反应组件 FormInput
。该组件使用 MUI 框架和 react-hook-form。问题是我无法在我的组件中为参数“control”设置正确的类型。我临时用 type any 合并了它。
在道具控件中输入的类型是类型Control<TrekEditDto, object>
。
文档中的 react-hook-form 是参数 control?: Control<FieldValues, object> | undefined
我认为解决方案是使用泛型。
这是我的反应输入组件:
import { Controller } from 'react-hook-form';
import { TextField, Typography } from '@material-ui/core';
import React from 'react';
type FormInputProps = {
control: any;
name: string;
label: string;
multiline?: boolean;
defaultValue?: string;
fullWidth?: boolean;
disabled?: boolean;
error?: { message: string };
};
const FormInput = ({ control, name, label, multiline, defaultValue, fullWidth, disabled, error }: FormInputProps) => {
return (
<>
<Controller
control={control}
name={name}
defaultValue={defaultValue}
render={(...inputProps) => {
return (
<>
<TextField
{...inputProps}
fullWidth={fullWidth}
label={label}
multiline={multiline}
disabled={disabled}
error={error != null}
/>
<Typography variant="inherit" color="textSecondary">
{error?.message}
</Typography>
</>
);
}}
/>
</>
);
};
export default FormInput;
这部分正在调用我的组件:
<FormInput
control={control}
name="translation.cz.name"
label={t('trek.create.formField.name.cs')}
/>
它是包含调用我的组件的完整文件。
import React, {useEffect, useState} from "react";
import TrekService from "../services/TrekService";
import {
Box,
Button,
Container, DialogActions, DialogContent, DialogContentText,
FormLabel,
Grid,
Paper,
Snackbar,
Typography
} from "@material-ui/core";
import {FormRadio} from "../components/FormRadio";
import {http} from "../../../common/axios-api";
import {useForm} from "react-hook-form";
import {useTranslation} from "react-i18next";
import {DropzoneArea} from "material-ui-dropzone";
import AlertDialog from "../../../components/AlertDialog/AlertDialog";
import {Alert} from "@material-ui/lab";
import {useHistory} from "react-router-dom";
import {Permissions} from "../../../auth/hooks/Permissions";
import ROLES from "../../../auth/Roles";
import {TYPE_OF_STATUS} from "../commons/TrekConstants";
import {FormAutocomplete} from "../components/FormAutocomplete";
import {FormDifficulty} from "../components/FormDifficulty";
import {FormExperiences} from "../components/FormExperiences";
import {TrekEditDto} from "../../../models/Trek";
import {Codebook} from "../../../models/Codebook";
import FormInput from "../components/FormInput";
import FormInputMultiline from "../components/FormInputMultiline";
export default function EditTrek() {
const {t} = useTranslation(['translation', 'common']);
const history = useHistory();
const [trek, setTrek] = useState<TrekEditDto>();
const [codebook, setCodebook] = useState<Codebook>();
const {handleSubmit, control, reset, setValue, watch, formState: {errors}} = useForm<TrekEditDto>({
defaultValues: trek
});
const [trekImages, setTrekImages] = useState<File[]>([]);
const [coverImage, setCoverImage] = useState<File[]>([]);
const [trekGpx, setTrekGpx] = useState<File[]>([]);
const [openSuccessDialog, setOpenSuccessDialog] = useState(false);
const [openErrorMessage, setOpenErrorMessage] = useState(false);
const watchTrekType = watch('trip_type_id');
// max file size is 2MB
const MAX_FILE_SIZE_FOR_UPLOAD = 2 * 1000 * 1000;
// max number of file for upload image
const MAX_NUMBER_FILES_FOR_UPLOAD = 6;
// max number gpx files for upload
const MAX_NUMBER_GPX_FILES_FOR_UPLOAD = 1;
useEffect(() => {
async function fetchData() {
try {
await loadCoodBook();
const trekId = (new URLSearchParams(window.location.search)).get('trekId');
if (trekId == null) {
throw new Error(`Couldnt load data becouse trek id = ${trekId}`);
}
await TrekService.findById(trekId)
.then(response => {
setTrek(response.data);
reset(response.data);
}
)
} catch (e) {
console.error(e)
}
}
fetchData();
}, []);
const loadCoodBook = () => {
http.get(`web/codebook`)
.then(response => {
setCodebook(response.data);
})
.catch(reason => {
console.error('Nastala chyba pri nacitani codebook: ', reason)
})
}
const publish = (data: TrekEditDto) => {
sendForm(data, TYPE_OF_STATUS.PUBLISHED);
};
const save = (data: TrekEditDto) => {
sendForm(data, TYPE_OF_STATUS.TO_CHECK);
};
const sendForm = async (data: TrekEditDto, status: number) => {
//convert data into correct format
const sendData = JSON.parse(JSON.stringify(data));
sendData.state_id = data.state_id?.id;
sendData['status'] = status;
if (data.danger != null) {
sendData.danger = data.danger.map(danger => danger.id);
}
if (data.equipment != null) {
sendData.equipment = data.equipment.map(equip => equip.id);
}
if (data.pois != null) {
sendData.poi = data.pois.map(poi => poi.id);
}
// workaround for backend - removed start_point and destination_point
delete sendData.start_point;
delete sendData.destination_point;
if (watchTrekType !== '2') {
delete sendData.check_point;
}
try {
const trekId = sendData?.trekId;
await TrekService.updateTrek(trekId, sendData);
// delete previous images
await trek?.images.map(img => {
const nameOfImage = img.substring(img.lastIndexOf('/') + 1, img.length);
TrekService.deleteTrekImage(trekId, nameOfImage);
});
// delete previous cover image
if (trek?.coverImage != undefined) {
const nameOfImage = trek?.coverImage.substring(trek?.coverImage.lastIndexOf('/') + 1, trek?.coverImage.length);
await TrekService.deleteCoverTrekImage(trekId, nameOfImage);
}
trekImages.length > 0 && trekId && trekImages.map(image => uploadTrekImagesFiles(trekId, image));
coverImage.length > 0 && trekId && await uploadTrekCoverImagesFiles(trekId, coverImage[0]);
trekGpx.length > 0 && trekId && await uploadTrekGpxFiles(trekId);
handleOpenSuccessDialog();
} catch (error) {
console.error('update trek error = ', error);
setOpenErrorMessage(true);
}
}
const uploadTrekGpxFiles = (trekId: number) => {
const formData = new FormData();
formData.append('file', trekGpx[0])
return TrekService.uploadTrekGpx(trekId, formData);
}
const uploadTrekImagesFiles = (trekId: number, image: File) => {
const formData = new FormData();
formData.append('file', image);
return TrekService.uploadTrekImage(trekId, formData);
}
const uploadTrekCoverImagesFiles = (trekId: number, image: File) => {
const formData = new FormData();
formData.append('file', image);
return TrekService.uploadTrekCoverImage(trekId, formData);
}
const handleCloseErrorMessage = () => {
setOpenErrorMessage(false);
}
const handleCloseSuccessDialog = () => {
setOpenSuccessDialog(false);
}
const handleOpenSuccessDialog = () => {
setOpenSuccessDialog(true);
}
return (
codebook && trek && Object.entries(trek).length > 0 && (
<Container>
<Paper>
<Box component="form" px={8} py={8}>
<Typography variant="h4"> {t('trek.edit.formTitle')}</Typography>
<Grid container spacing={3}>
<Grid item xs={12} sm={12}>
<FormInput
control={control}
name="translation.cz.name"
label={t('trek.create.formField.name.cs')}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormInput
control={control}
name="translation.en.name"
label={t('trek.create.formField.name.en')}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormInput
control={control}
name="translation.cz.title"
label={t('trek.create.formField.title.cs')}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormInput
control={control}
name="translation.en.title"
label={t('trek.create.formField.title.en')}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormInputMultiline
control={control}
name="translation.cz.description"
label={t('trek.create.formField.description.cs')}
maxRows={6}
minRows={6}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormInputMultiline
control={control}
name="translation.en.description"
label={t('trek.create.formField.description.en')}
maxRows={6}
minRows={6}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormInputMultiline
control={control}
name="note"
label={t('trek.create.formField.note')}
maxRows={6}
minRows={1}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormInput
control={control}
name="duration"
label={t('trek.create.formField.duration')}
required={true}
error={errors.duration}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormInput
control={control}
name="distance"
label={t('trek.create.formField.distance')}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormInput
control={control}
name="elevation_difference"
label={t('trek.create.formField.elevationDifference')}
/>
</Grid>
<FormDifficulty control={control} setValue={setValue} menuItems={codebook?.difficulty}/>
<FormExperiences control={control} setValue={setValue}/>
<Grid item xs={12} sm={12}>
<FormInput
control={control}
name="highest_point"
label={t('trek.create.formField.highestPoint')}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormInput
control={control}
name="link"
label={t('trek.create.formField.link')}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormRadio name='trip_type_id'
control={control}
label={t('trek.create.formField.typeOfTrek')}
menuItems={codebook?.tripType}
setValue={setValue}
/>
</Grid>
{watchTrekType === '2' && (
<>
<Grid item xs={12} sm={12}>
<FormInput
control={control}
name="check_point.lat"
label={t('trek.create.formField.latitude')}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormInput
control={control}
name="check_point.long"
label={t('trek.create.formField.longitude')}
/>
</Grid>
</>
)}
<Grid item xs={12} sm={12}>
<FormAutocomplete label={t('trek.create.formField.state')}
control={control}
name="state_id"
options={codebook?.state}
multiple={false}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormAutocomplete label={t('trek.create.formField.danger')}
control={control}
name="danger"
options={codebook?.danger}
multiple={true}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormAutocomplete label={t('trek.create.formField.equipment')}
control={control}
name="equipment"
options={codebook?.equipment}
multiple={true}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormAutocomplete label={t('trek.create.formField.poi')}
control={control}
name="pois"
options={codebook?.poi}
multiple={true}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormRadio name='camping_id'
control={control}
label={t('trek.create.formField.camping')}
menuItems={codebook?.camping}
setValue={setValue}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormRadio name='dog_friendly_id'
control={control}
label={t('trek.create.formField.dogFriendly')}
menuItems={codebook?.dogFriendly}
setValue={setValue}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormLabel
component="legend">{t('trek.create.formField.coverImage', {
imageCount: 1,
imageMaxSize: MAX_FILE_SIZE_FOR_UPLOAD / 1000000
})}</FormLabel>
<DropzoneArea
name={'coverImage'}
filesLimit={1}
acceptedFiles={['image/*']}
onChange={(files) => {
setCoverImage(files)
}}
maxFileSize={MAX_FILE_SIZE_FOR_UPLOAD}
dropzoneText={t('material-ui-dropzone.dropzonetext')}
initialFiles={[trek?.coverImage]}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormLabel
component="legend">{t('trek.create.formField.images', {
imageCount: MAX_NUMBER_FILES_FOR_UPLOAD,
imageMaxSize: MAX_FILE_SIZE_FOR_UPLOAD / 1000000
})}</FormLabel>
<DropzoneArea
name={'trekImages'}
filesLimit={MAX_NUMBER_FILES_FOR_UPLOAD}
acceptedFiles={['image/*']}
onChange={(files) => {
setTrekImages(files)
}}
maxFileSize={MAX_FILE_SIZE_FOR_UPLOAD}
dropzoneText={t('material-ui-dropzone.dropzonetext')}
initialFiles={trek?.images}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormLabel component="legend">{t('trek.create.formField.gpx')}</FormLabel>
<DropzoneArea
name={'trekGpx'}
filesLimit={MAX_NUMBER_GPX_FILES_FOR_UPLOAD}
// acceptedFiles={['application/gpx+xml']}
onChange={(files) => {
setTrekGpx(files)
}}
dropzoneText={t('material-ui-dropzone.dropzonetext')}
/>
</Grid>
<Grid
container
direction="row"
justifyContent="center"
>
<Button variant={"contained"}
onClick={handleSubmit(save)}>{t('trek.create.form.button.submit.save')}</Button>
<Permissions roles={[ROLES.admin, ROLES.approver]}>
<Button variant={"contained"}
onClick={handleSubmit(publish)}>{t('trek.create.form.button.submit.publish')}</Button>
</Permissions>
</Grid>
</Grid>
<Snackbar open={openErrorMessage} autoHideDuration={6000} onClose={handleCloseErrorMessage}>
<Alert onClose={handleCloseErrorMessage} severity="error">
{t('trek.edit.message.error')}
</Alert>
</Snackbar>
<AlertDialog open={openSuccessDialog}>
<DialogContent>
<DialogContentText id="alert-dialog-description">
{t('trek.edit.message.success')}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleCloseSuccessDialog} color="primary">
{t('trek.edit.message.button.edit')}
</Button>
<Button onClick={() => history.push('/adventurerClient/treks')} color="primary">
{t('trek.edit.message.button.goToTrekList')}
</Button>
</DialogActions>
</AlertDialog>
</Box>
</Paper>
</Container>
)
)
}
我认为文档非常清楚,您必须将表单值用作类型。
type FormValues = {
'translation.cz.name': string
}
type FormInputProps = {
control: Control<FormValues>
// ...
}
尽管我希望不必告诉您,如果这个“translation.cz.name”字符串是可替换的,您的表单处理逻辑就会遇到麻烦。 name
属性不应暴露给最终用户,因此绝对没有必要对其进行本地化。
无论如何,如果您需要更多帮助,请提供 minimal reproducible example, preferably in codesandbox.io。您的问题中有很多代码,但实际上我们 运行 一个也做不到。
我为我的表单创建了一个打字稿反应组件 FormInput
。该组件使用 MUI 框架和 react-hook-form。问题是我无法在我的组件中为参数“control”设置正确的类型。我临时用 type any 合并了它。
在道具控件中输入的类型是类型Control<TrekEditDto, object>
。
文档中的 react-hook-form 是参数 control?: Control<FieldValues, object> | undefined
我认为解决方案是使用泛型。
这是我的反应输入组件:
import { Controller } from 'react-hook-form';
import { TextField, Typography } from '@material-ui/core';
import React from 'react';
type FormInputProps = {
control: any;
name: string;
label: string;
multiline?: boolean;
defaultValue?: string;
fullWidth?: boolean;
disabled?: boolean;
error?: { message: string };
};
const FormInput = ({ control, name, label, multiline, defaultValue, fullWidth, disabled, error }: FormInputProps) => {
return (
<>
<Controller
control={control}
name={name}
defaultValue={defaultValue}
render={(...inputProps) => {
return (
<>
<TextField
{...inputProps}
fullWidth={fullWidth}
label={label}
multiline={multiline}
disabled={disabled}
error={error != null}
/>
<Typography variant="inherit" color="textSecondary">
{error?.message}
</Typography>
</>
);
}}
/>
</>
);
};
export default FormInput;
这部分正在调用我的组件:
<FormInput
control={control}
name="translation.cz.name"
label={t('trek.create.formField.name.cs')}
/>
它是包含调用我的组件的完整文件。
import React, {useEffect, useState} from "react";
import TrekService from "../services/TrekService";
import {
Box,
Button,
Container, DialogActions, DialogContent, DialogContentText,
FormLabel,
Grid,
Paper,
Snackbar,
Typography
} from "@material-ui/core";
import {FormRadio} from "../components/FormRadio";
import {http} from "../../../common/axios-api";
import {useForm} from "react-hook-form";
import {useTranslation} from "react-i18next";
import {DropzoneArea} from "material-ui-dropzone";
import AlertDialog from "../../../components/AlertDialog/AlertDialog";
import {Alert} from "@material-ui/lab";
import {useHistory} from "react-router-dom";
import {Permissions} from "../../../auth/hooks/Permissions";
import ROLES from "../../../auth/Roles";
import {TYPE_OF_STATUS} from "../commons/TrekConstants";
import {FormAutocomplete} from "../components/FormAutocomplete";
import {FormDifficulty} from "../components/FormDifficulty";
import {FormExperiences} from "../components/FormExperiences";
import {TrekEditDto} from "../../../models/Trek";
import {Codebook} from "../../../models/Codebook";
import FormInput from "../components/FormInput";
import FormInputMultiline from "../components/FormInputMultiline";
export default function EditTrek() {
const {t} = useTranslation(['translation', 'common']);
const history = useHistory();
const [trek, setTrek] = useState<TrekEditDto>();
const [codebook, setCodebook] = useState<Codebook>();
const {handleSubmit, control, reset, setValue, watch, formState: {errors}} = useForm<TrekEditDto>({
defaultValues: trek
});
const [trekImages, setTrekImages] = useState<File[]>([]);
const [coverImage, setCoverImage] = useState<File[]>([]);
const [trekGpx, setTrekGpx] = useState<File[]>([]);
const [openSuccessDialog, setOpenSuccessDialog] = useState(false);
const [openErrorMessage, setOpenErrorMessage] = useState(false);
const watchTrekType = watch('trip_type_id');
// max file size is 2MB
const MAX_FILE_SIZE_FOR_UPLOAD = 2 * 1000 * 1000;
// max number of file for upload image
const MAX_NUMBER_FILES_FOR_UPLOAD = 6;
// max number gpx files for upload
const MAX_NUMBER_GPX_FILES_FOR_UPLOAD = 1;
useEffect(() => {
async function fetchData() {
try {
await loadCoodBook();
const trekId = (new URLSearchParams(window.location.search)).get('trekId');
if (trekId == null) {
throw new Error(`Couldnt load data becouse trek id = ${trekId}`);
}
await TrekService.findById(trekId)
.then(response => {
setTrek(response.data);
reset(response.data);
}
)
} catch (e) {
console.error(e)
}
}
fetchData();
}, []);
const loadCoodBook = () => {
http.get(`web/codebook`)
.then(response => {
setCodebook(response.data);
})
.catch(reason => {
console.error('Nastala chyba pri nacitani codebook: ', reason)
})
}
const publish = (data: TrekEditDto) => {
sendForm(data, TYPE_OF_STATUS.PUBLISHED);
};
const save = (data: TrekEditDto) => {
sendForm(data, TYPE_OF_STATUS.TO_CHECK);
};
const sendForm = async (data: TrekEditDto, status: number) => {
//convert data into correct format
const sendData = JSON.parse(JSON.stringify(data));
sendData.state_id = data.state_id?.id;
sendData['status'] = status;
if (data.danger != null) {
sendData.danger = data.danger.map(danger => danger.id);
}
if (data.equipment != null) {
sendData.equipment = data.equipment.map(equip => equip.id);
}
if (data.pois != null) {
sendData.poi = data.pois.map(poi => poi.id);
}
// workaround for backend - removed start_point and destination_point
delete sendData.start_point;
delete sendData.destination_point;
if (watchTrekType !== '2') {
delete sendData.check_point;
}
try {
const trekId = sendData?.trekId;
await TrekService.updateTrek(trekId, sendData);
// delete previous images
await trek?.images.map(img => {
const nameOfImage = img.substring(img.lastIndexOf('/') + 1, img.length);
TrekService.deleteTrekImage(trekId, nameOfImage);
});
// delete previous cover image
if (trek?.coverImage != undefined) {
const nameOfImage = trek?.coverImage.substring(trek?.coverImage.lastIndexOf('/') + 1, trek?.coverImage.length);
await TrekService.deleteCoverTrekImage(trekId, nameOfImage);
}
trekImages.length > 0 && trekId && trekImages.map(image => uploadTrekImagesFiles(trekId, image));
coverImage.length > 0 && trekId && await uploadTrekCoverImagesFiles(trekId, coverImage[0]);
trekGpx.length > 0 && trekId && await uploadTrekGpxFiles(trekId);
handleOpenSuccessDialog();
} catch (error) {
console.error('update trek error = ', error);
setOpenErrorMessage(true);
}
}
const uploadTrekGpxFiles = (trekId: number) => {
const formData = new FormData();
formData.append('file', trekGpx[0])
return TrekService.uploadTrekGpx(trekId, formData);
}
const uploadTrekImagesFiles = (trekId: number, image: File) => {
const formData = new FormData();
formData.append('file', image);
return TrekService.uploadTrekImage(trekId, formData);
}
const uploadTrekCoverImagesFiles = (trekId: number, image: File) => {
const formData = new FormData();
formData.append('file', image);
return TrekService.uploadTrekCoverImage(trekId, formData);
}
const handleCloseErrorMessage = () => {
setOpenErrorMessage(false);
}
const handleCloseSuccessDialog = () => {
setOpenSuccessDialog(false);
}
const handleOpenSuccessDialog = () => {
setOpenSuccessDialog(true);
}
return (
codebook && trek && Object.entries(trek).length > 0 && (
<Container>
<Paper>
<Box component="form" px={8} py={8}>
<Typography variant="h4"> {t('trek.edit.formTitle')}</Typography>
<Grid container spacing={3}>
<Grid item xs={12} sm={12}>
<FormInput
control={control}
name="translation.cz.name"
label={t('trek.create.formField.name.cs')}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormInput
control={control}
name="translation.en.name"
label={t('trek.create.formField.name.en')}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormInput
control={control}
name="translation.cz.title"
label={t('trek.create.formField.title.cs')}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormInput
control={control}
name="translation.en.title"
label={t('trek.create.formField.title.en')}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormInputMultiline
control={control}
name="translation.cz.description"
label={t('trek.create.formField.description.cs')}
maxRows={6}
minRows={6}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormInputMultiline
control={control}
name="translation.en.description"
label={t('trek.create.formField.description.en')}
maxRows={6}
minRows={6}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormInputMultiline
control={control}
name="note"
label={t('trek.create.formField.note')}
maxRows={6}
minRows={1}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormInput
control={control}
name="duration"
label={t('trek.create.formField.duration')}
required={true}
error={errors.duration}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormInput
control={control}
name="distance"
label={t('trek.create.formField.distance')}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormInput
control={control}
name="elevation_difference"
label={t('trek.create.formField.elevationDifference')}
/>
</Grid>
<FormDifficulty control={control} setValue={setValue} menuItems={codebook?.difficulty}/>
<FormExperiences control={control} setValue={setValue}/>
<Grid item xs={12} sm={12}>
<FormInput
control={control}
name="highest_point"
label={t('trek.create.formField.highestPoint')}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormInput
control={control}
name="link"
label={t('trek.create.formField.link')}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormRadio name='trip_type_id'
control={control}
label={t('trek.create.formField.typeOfTrek')}
menuItems={codebook?.tripType}
setValue={setValue}
/>
</Grid>
{watchTrekType === '2' && (
<>
<Grid item xs={12} sm={12}>
<FormInput
control={control}
name="check_point.lat"
label={t('trek.create.formField.latitude')}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormInput
control={control}
name="check_point.long"
label={t('trek.create.formField.longitude')}
/>
</Grid>
</>
)}
<Grid item xs={12} sm={12}>
<FormAutocomplete label={t('trek.create.formField.state')}
control={control}
name="state_id"
options={codebook?.state}
multiple={false}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormAutocomplete label={t('trek.create.formField.danger')}
control={control}
name="danger"
options={codebook?.danger}
multiple={true}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormAutocomplete label={t('trek.create.formField.equipment')}
control={control}
name="equipment"
options={codebook?.equipment}
multiple={true}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormAutocomplete label={t('trek.create.formField.poi')}
control={control}
name="pois"
options={codebook?.poi}
multiple={true}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormRadio name='camping_id'
control={control}
label={t('trek.create.formField.camping')}
menuItems={codebook?.camping}
setValue={setValue}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormRadio name='dog_friendly_id'
control={control}
label={t('trek.create.formField.dogFriendly')}
menuItems={codebook?.dogFriendly}
setValue={setValue}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormLabel
component="legend">{t('trek.create.formField.coverImage', {
imageCount: 1,
imageMaxSize: MAX_FILE_SIZE_FOR_UPLOAD / 1000000
})}</FormLabel>
<DropzoneArea
name={'coverImage'}
filesLimit={1}
acceptedFiles={['image/*']}
onChange={(files) => {
setCoverImage(files)
}}
maxFileSize={MAX_FILE_SIZE_FOR_UPLOAD}
dropzoneText={t('material-ui-dropzone.dropzonetext')}
initialFiles={[trek?.coverImage]}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormLabel
component="legend">{t('trek.create.formField.images', {
imageCount: MAX_NUMBER_FILES_FOR_UPLOAD,
imageMaxSize: MAX_FILE_SIZE_FOR_UPLOAD / 1000000
})}</FormLabel>
<DropzoneArea
name={'trekImages'}
filesLimit={MAX_NUMBER_FILES_FOR_UPLOAD}
acceptedFiles={['image/*']}
onChange={(files) => {
setTrekImages(files)
}}
maxFileSize={MAX_FILE_SIZE_FOR_UPLOAD}
dropzoneText={t('material-ui-dropzone.dropzonetext')}
initialFiles={trek?.images}
/>
</Grid>
<Grid item xs={12} sm={12}>
<FormLabel component="legend">{t('trek.create.formField.gpx')}</FormLabel>
<DropzoneArea
name={'trekGpx'}
filesLimit={MAX_NUMBER_GPX_FILES_FOR_UPLOAD}
// acceptedFiles={['application/gpx+xml']}
onChange={(files) => {
setTrekGpx(files)
}}
dropzoneText={t('material-ui-dropzone.dropzonetext')}
/>
</Grid>
<Grid
container
direction="row"
justifyContent="center"
>
<Button variant={"contained"}
onClick={handleSubmit(save)}>{t('trek.create.form.button.submit.save')}</Button>
<Permissions roles={[ROLES.admin, ROLES.approver]}>
<Button variant={"contained"}
onClick={handleSubmit(publish)}>{t('trek.create.form.button.submit.publish')}</Button>
</Permissions>
</Grid>
</Grid>
<Snackbar open={openErrorMessage} autoHideDuration={6000} onClose={handleCloseErrorMessage}>
<Alert onClose={handleCloseErrorMessage} severity="error">
{t('trek.edit.message.error')}
</Alert>
</Snackbar>
<AlertDialog open={openSuccessDialog}>
<DialogContent>
<DialogContentText id="alert-dialog-description">
{t('trek.edit.message.success')}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleCloseSuccessDialog} color="primary">
{t('trek.edit.message.button.edit')}
</Button>
<Button onClick={() => history.push('/adventurerClient/treks')} color="primary">
{t('trek.edit.message.button.goToTrekList')}
</Button>
</DialogActions>
</AlertDialog>
</Box>
</Paper>
</Container>
)
)
}
我认为文档非常清楚,您必须将表单值用作类型。
type FormValues = {
'translation.cz.name': string
}
type FormInputProps = {
control: Control<FormValues>
// ...
}
尽管我希望不必告诉您,如果这个“translation.cz.name”字符串是可替换的,您的表单处理逻辑就会遇到麻烦。 name
属性不应暴露给最终用户,因此绝对没有必要对其进行本地化。
无论如何,如果您需要更多帮助,请提供 minimal reproducible example, preferably in codesandbox.io。您的问题中有很多代码,但实际上我们 运行 一个也做不到。