javascript - 能够从 API 接收数据,但无法保存到表单字段上显示的状态
问题描述
import nextConnect from 'next-connect';
import { isAuth, isAdmin } from '../../../utils/auth';
import { onError } from '../../../utils/error';
import multer from 'multer';
import { v2 as cloudinary } from 'cloudinary';
import streamifier from 'streamifier';
cloudinary.config({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
});
export const config = {
api: {
bodyParser: false,
},
};
const handler = nextConnect({ onError });
const upload = multer();
handler.use(isAuth, isAdmin, upload.single('file')).post(async (req, res) => {
const streamUpload = (req) => {
return new Promise((resolve, reject) => {
const stream = cloudinary.uploader.upload_stream((error, result) => {
if (result) {
resolve(result);
} else {
reject(error);
}
});
streamifier.createReadStream(req.file.buffer).pipe(stream);
});
};
const result = await streamUpload(req);
// console.log(result);
res.send(result);
});
export default handler;
import axios from 'axios';
import dynamic from 'next/dynamic';
import { useRouter } from 'next/router';
import NextLink from 'next/link';
import React, { useEffect, useContext, useReducer } from 'react';
import {
Grid,
List,
ListItem,
Typography,
Card,
Button,
ListItemText,
TextField,
CircularProgress,
} from '@mui/material';
import { getError } from '../../../utils/error';
import { Store } from '../../../utils/Store';
import Layout from '../../../components/Layout';
import useStyles from '../../../utils/styles';
import { Controller, useForm } from 'react-hook-form';
import { useSnackbar } from 'notistack';
function reducer(state, action) {
switch (action.type) {
case 'FETCH_REQUEST':
return { ...state, loading: true, error: '' };
case 'FETCH_SUCCESS':
return { ...state, loading: false, error: '' };
case 'FETCH_FAIL':
return { ...state, loading: false, error: action.payload };
case 'UPDATE_REQUEST':
return { ...state, loadingUpdate: true, errorUpdate: '' };
case 'UPDATE_SUCCESS':
return { ...state, loadingUpdate: false, errorUpdate: '' };
case 'UPDATE_FAIL':
return { ...state, loadingUpdate: false, errorUpdate: action.payload };
case 'UPLOAD_REQUEST':
return { ...state, loadingUpload: true, errorUpload: '' };
case 'UPLOAD_SUCCESS':
return {
...state,
loadingUpload: false,
errorUpload: '',
};
case 'UPLOAD_FAIL':
return { ...state, loadingUpload: false, errorUpload: action.payload };
default:
return state;
}
}
function ProductEdit({ params }) {
const productId = params.id;
const { state } = useContext(Store);
const [{ loading, error, loadingUpdate, loadingUpload }, dispatch] =
useReducer(reducer, {
loading: true,
error: '',
});
const {
handleSubmit,
control,
formState: { errors },
setValue,
} = useForm();
const { enqueueSnackbar, closeSnackbar } = useSnackbar();
const router = useRouter();
const classes = useStyles();
const { userInfo } = state;
useEffect(() => {
if (!userInfo) {
return router.push('/login');
} else {
const fetchData = async () => {
try {
dispatch({ type: 'FETCH_REQUEST' });
const { data } = await axios.get(`/api/admin/products/${productId}`, {
headers: { authorization: `Bearer ${userInfo.token}` },
});
dispatch({ type: 'FETCH_SUCCESS' });
setValue('name', data.name);
setValue('slug', data.slug);
setValue('price', data.price);
setValue('image', data.image);
setValue('category', data.category);
setValue('brand', data.brand);
setValue('countInStock', data.countInStock);
setValue('description', data.description);
} catch (err) {
dispatch({ type: 'FETCH_FAIL', payload: getError(err) });
}
};
fetchData();
}
}, []);
const uploadHandler = async (e, imageField = 'image') => {
const file = e.target.files[0];
const bodyFormData = new FormData();
bodyFormData.append('file', file);
try {
dispatch({ type: 'UPLOAD_REQUEST' });
const { data } = await axios.post('/api/admin/upload', bodyFormData, {
headers: {
'Content-Type': 'multipart/form-data',
authorization: `Bearer ${userInfo.token}`,
},
});
dispatch({ type: 'UPLOAD_SUCCESS' });
enqueueSnackbar('File uploaded successfully', { variant: 'success' });
setValue(imageField, data.secure_url);
} catch (err) {
dispatch({ type: 'UPLOAD_FAIL', payload: getError(err) });
enqueueSnackbar(getError(err), { variant: 'error' });
}
};
const submitHandler = async ({
name,
slug,
price,
category,
image,
brand,
countInStock,
description,
}) => {
closeSnackbar();
try {
dispatch({ type: 'UPDATE_REQUEST' });
await axios.put(
`/api/admin/products/${productId}`,
{
name,
slug,
price,
category,
image,
brand,
countInStock,
description,
},
{ headers: { authorization: `Bearer ${userInfo.token}` } }
);
dispatch({ type: 'UPDATE_SUCCESS' });
enqueueSnackbar('Product updated successfully', { variant: 'success' });
router.push('/admin/products');
} catch (err) {
dispatch({ type: 'UPDATE_FAIL', payload: getError(err) });
enqueueSnackbar(getError(err), { variant: 'error' });
}
};
return (
<Layout title={`Edit Product ${productId}`}>
<Grid container spacing={1}>
<Grid item md={3} xs={12}>
<Card className={classes.section}>
<List>
<NextLink href="/admin/dashboard" passHref>
<ListItem button component="a">
<ListItemText primary="Admin Dashboard"></ListItemText>
</ListItem>
</NextLink>
<NextLink href="/admin/orders" passHref>
<ListItem button component="a">
<ListItemText primary="Orders"></ListItemText>
</ListItem>
</NextLink>
<NextLink href="/admin/products" passHref>
<ListItem selected button component="a">
<ListItemText primary="Products"></ListItemText>
</ListItem>
</NextLink>
<NextLink href="/admin/users" passHref>
<ListItem button component="a">
<ListItemText primary="Users"></ListItemText>
</ListItem>
</NextLink>
</List>
</Card>
</Grid>
<Grid item md={9} xs={12}>
<Card className={classes.section}>
<List>
<ListItem>
<Typography component="h1" variant="h1">
Edit Product {productId}
</Typography>
</ListItem>
<ListItem>
{loading && <CircularProgress></CircularProgress>}
{error && (
<Typography className={classes.error}>{error}</Typography>
)}
</ListItem>
<ListItem>
<form
onSubmit={handleSubmit(submitHandler)}
className={classes.form}
>
<List>
<ListItem>
<Controller
name="name"
control={control}
value=""
rules={{
required: true,
}}
render={({ field }) => (
<TextField
variant="outlined"
fullWidth
id="name"
placeholder="Name"
error={Boolean(errors.name)}
helperText={errors.name ? 'Name is required' : ''}
{...field}
></TextField>
)}
></Controller>
</ListItem>
<ListItem>
<Controller
name="slug"
control={control}
value=""
rules={{
required: true,
}}
render={({ field }) => (
<TextField
variant="outlined"
fullWidth
id="slug"
placeholder="Slug"
error={Boolean(errors.slug)}
helperText={errors.slug ? 'Slug is required' : ''}
{...field}
></TextField>
)}
></Controller>
</ListItem>
<ListItem>
<Controller
name="price"
control={control}
value=""
rules={{
required: true,
}}
render={({ field }) => (
<TextField
variant="outlined"
fullWidth
id="price"
placeholder="Price"
error={Boolean(errors.price)}
helperText={errors.price ? 'Price is required' : ''}
{...field}
></TextField>
)}
></Controller>
</ListItem>
<ListItem>
<Controller
name="image"
control={control}
value=""
rules={{
required: true,
}}
render={({ field }) => (
<TextField
variant="outlined"
fullWidth
id="image"
placeholder="Image URL"
error={Boolean(errors.image)}
helperText={errors.image ? 'Image is required' : ''}
{...field}
></TextField>
)}
></Controller>
</ListItem>
<ListItem>
<Button variant="contained" component="label">
Upload File
<input type="file" onChange={uploadHandler} hidden />
</Button>
{loadingUpload && <CircularProgress />}
</ListItem>
<ListItem>
<Controller
name="category"
control={control}
value=""
rules={{
required: true,
}}
render={({ field }) => (
<TextField
variant="outlined"
fullWidth
id="category"
placeholder="Category"
error={Boolean(errors.category)}
helperText={
errors.category ? 'Category is required' : ''
}
{...field}
></TextField>
)}
></Controller>
</ListItem>
<ListItem>
<Controller
name="brand"
control={control}
value=""
rules={{
required: true,
}}
render={({ field }) => (
<TextField
variant="outlined"
fullWidth
id="brand"
placeholder="Brand"
error={Boolean(errors.brand)}
helperText={errors.brand ? 'Brand is required' : ''}
{...field}
></TextField>
)}
></Controller>
</ListItem>
<ListItem>
<Controller
name="countInStock"
control={control}
value=""
rules={{
required: true,
}}
render={({ field }) => (
<TextField
variant="outlined"
fullWidth
id="countInStock"
placeholder="Count in stock"
error={Boolean(errors.countInStock)}
helperText={
errors.countInStock
? 'Count in stock is required'
: ''
}
{...field}
></TextField>
)}
></Controller>
</ListItem>
<ListItem>
<Controller
name="description"
control={control}
value=""
rules={{
required: true,
}}
render={({ field }) => (
<TextField
variant="outlined"
fullWidth
multiline
id="description"
placeholder="Description"
error={Boolean(errors.description)}
helperText={
errors.description
? 'Description is required'
: ''
}
{...field}
></TextField>
)}
></Controller>
</ListItem>
<ListItem>
<Button
variant="contained"
type="submit"
fullWidth
color="primary"
>
Update
</Button>
{loadingUpdate && <CircularProgress />}
</ListItem>
</List>
</form>
</ListItem>
</List>
</Card>
</Grid>
</Grid>
</Layout>
);
}
export async function getServerSideProps({ params }) {
return {
props: { params },
};
}
export default dynamic(() => Promise.resolve(ProductEdit), { ssr: false });
我能够从 cloudinary 接收数据,但无法从另一个 js 文件中检索 uploadHandler。我有来自 res.send(result) 的 console.log 输出;但是当我在代码下方成功调度时,它没有更新状态。然后我尝试 console.log {data} 那里什么都没有。Console.log 结果返回如下图。
dispatch({ type: 'UPLOAD_SUCCESS' });
enqueueSnackbar('File uploaded successfully', { variant: 'success' });
setValue(imageField, data.secure_url);
解决方案
推荐阅读
- python - 使用来自 scikit learn 的 LabelEncoder,有没有办法改变对特定列的值进行编码的方式?
- java - 是否可以在没有 Maven / Gradle 的情况下创建 Google App Engine 应用程序?
- python - Webscraper 突然停止工作。云票价的潜在问题?
- python - 在Python中使用距离矩阵计算经纬度点之间的距离
- django - 如何在 Django 中添加 ArrayField?
- javascript - 在 HTML 属性中调用带有字符串参数的函数的正确语法是什么?
- excel - 需要通过文本和数字组合找到最大值
- css - 创建宽度等于高度的div
- python - pyspark - 将 udf 应用于我的数据帧流式查询时,我收到错误“正在中止编写作业”
- python - 准确的时间