首页 > 解决方案 > 能够从 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);

在此处输入图像描述

在此处输入图像描述

标签: javascriptreactjsnext.jsmulter

解决方案


推荐阅读