Как предварительно загрузить изображение в зону реакции? - PullRequest
0 голосов
/ 20 апреля 2020

В моем проекте я использую React + Redux, а для обработки форм - избыточные формы. Я был в состоянии предварительно загрузить поля в избыточную форму (с целью обновления их значений), однако я не смог сделать то же самое для полей изображения, используя реакционную зону сброса. Вот что я вижу на странице, когда загружаю форму:

prefilled form values - image not prefilled

И я надеюсь, что существующее изображение также будет предварительно заполнено. Данные предоставляются в форме, подобной этой:

{
    "name":"Salted Butter",
    "cost":"4",
    "quantifier":"grams",
    "servings":"250",
    "quantity":"250",
    "img":"data:image/jpeg;base64,/9j/4AAQSkZJRgABA...."
}

Это то, что у меня есть до сих пор:

ImageInput.jsx (компонент передан в избыточную форму поле)

import React from 'react';
import ImageInputPreview from "../../atoms/placeholders/ImageInputPreview";
import ImageInputPlaceholder from "../../atoms/placeholders/ImageInputPlaceholder";
import DropZone from 'react-dropzone';


const ImageInput = ({ handleOnDrop, input: { onChange }, imagefile, meta }) => (
  <div>
    <DropZone
      accept="image/jpeg, image/png, image/gif, image/bmp"
      onDrop={(file) => {
        handleOnDrop(file, onChange);
      }}
    >
      {({ getRootProps, getInputProps }) =>
        imagefile && imagefile.length > 0 ? (
          <ImageInputPreview imagefile={imagefile} />
        ) : (
          <ImageInputPlaceholder
            error={meta.error}
            touched={meta.touched}
            getInputProps={getInputProps}
            getRootProps={getRootProps}
          />
        )
      }
    </DropZone>
  </div>
);



export default ImageInput;

IngredientForm.jsx (Форма, используемая для создания и редактирования ингредиентов)

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { reduxForm, Field } from 'redux-form';
import AbstractTextInput from '../../atoms/forms/AbstractTextInput';
import CurrencyInput from '../../atoms/forms/CurrencyInput';
import QuantityInput from '../../atoms/forms/QuantityInput';
import TitleInput from '../../atoms/forms/TitleInput';
import SubmitButton from '../../atoms/buttons/SubmitButton';
import { upsertIngredient, getIngredient } from '../../../actions/ingredient';
import { withRouter } from 'react-router';
import ImageInput from "../../molecules/forms/ImageInput";


class IngredientForm extends Component {
    state= { imageFile: [] };

    // Preloads react-redux form if provided on props.preload
    componentDidMount = async () => {
        if (this.props.preload) {
            this.props.initialize(this.props.preload);
        }
    }

    // ImageInput function for handling image upload
    handleOnDrop = (newImageFile, onChange) => {
        const imageFile = {
            file: newImageFile[0],
            name: newImageFile[0].name,
            preview: URL.createObjectURL(newImageFile[0]),
            size: newImageFile[0].size
        };
        console.log('upload:', imageFile);

        this.setState({ imageFile: [imageFile] }, () => {
            onChange(imageFile) 
        });
    };

    // Handle form submissions
    onSubmit = async formValues => {
        // Convert image to base64
        const file = this.state.imageFile[0].file;
        const reader = new FileReader();
        let fileAsBase64 = '';

        reader.onload = () => {
            fileAsBase64 = reader.result;
        }
        reader.onabort = () => console.log("file reading was aborted");
        reader.onerror = () => console.log("file reading has failed");
        reader.readAsDataURL(file);
        reader.onloadend = () => {
            console.log(fileAsBase64);
            // Call action for handling form input
            return this.props.upsertIngredient({...formValues, img: fileAsBase64 });
        }
    }

    /** Render the form. */
    render() {
        return (
            <form onSubmit={this.props.handleSubmit((formValues) => this.onSubmit(formValues))}>
                <Field
                    name="img"
                    component={ImageInput}
                    type="file"
                    imagefile={this.state.imageFile}
                    prefill={this.startWithImage}
                    handleOnDrop={this.handleOnDrop}
                />
                <Field name='name' component={TitleInput} placeholder='Ingredient Name' />
                <Field name='cost' component={CurrencyInput} placeholder='Cost' />
                <Field name='quantity' component={QuantityInput} placeholder='Quantity' />
                <Field name='quantifier' component={AbstractTextInput} placeholder='cups/slices/grams/etc.' />
                <SubmitButton type='submit' >{this.props.preload ? 'Update' : 'Create'}</SubmitButton>
            </form>
        );
    }

}


/** Validates form fields and passes errors to redux-form to process. */
const validate = (formValues) => {
    const errors = {};
    // Required fields
    if (!formValues.name) { errors.name = 'You must enter an ingredient name'; }
    if (!formValues.cost) { errors.cost = 'You must enter a cost'; }
    if (!formValues.quantity) { errors.quantity = 'You must enter a quantity'; }
    if (!formValues.quantifier) { errors.quantifier = 'You must enter a quantifier'; }
    // Cost validation
    if ( isNaN(formValues.cost) ) { errors.cost = 'Cost must be a number'; }
    if ( formValues.cost < 0 ) { errors.cost = 'Cost must be at least 0'; }
    // Quantity validation
    if ( isNaN(formValues.quantity) ) { errors.quantity = 'Quantity must be a number'; }
    if ( formValues.quantity <= 0 ) { errors.quantity = 'Quantity must be greater than 0'; }

    return errors;
}


/** Removes values from form on successful submit. */
const onSubmitSuccess = (result, dispatch, props) => {
    props.history.push(`/ingredients`);
}


/** Configure the redux-form and wrap the component. */
const formWrapped = reduxForm({
    form: 'IngredientForm',
    validate,
    onSubmitSuccess
})(IngredientForm);


// Wrapping Order: withRouter > connect > formWrapped > component
export default withRouter(
    connect(null, { upsertIngredient })(formWrapped)
);

IngredientEditPage.jsx (A страница, которая предварительно загружает значения в форму)

import React, { Component } from 'react';
import { connect } from 'react-redux';
import IngredientForm from '../../organisms/forms/IngredientForm';
import Container from '../../molecules/layout/Container';
import PageTitle from '../../atoms/text/PageTitle';
import { getIngredient } from '../../../actions/ingredient';


class IngredientEditPage extends Component {

    componentDidMount = () => {
        const { getIngredient } = this.props;

        getIngredient(this.props.match.params.name);
    }

    render () { 
        return (
            <Container paddingTop={50} paddingBottom={50} >
                <PageTitle>Edit Ingredient</PageTitle>
                { this.props.formData 
                    && <IngredientForm preload={this.props.formData} /> }
            </Container>
        );
    }

}

const mapStateToProps = ({ ingredients }, { match }) => {
    const name = match.params.name;
    return { formData: ingredients[name] };
}

export default connect(mapStateToProps, { getIngredient })(IngredientEditPage);
...