在我的项目中,我使用React + Redux和redux-forms处理表单。我已经能够将字段预加载为redux形式(以更新其值),但是我无法使用react-dropzone对图像字段执行相同的操作。这是我在加载表单时在页面上显示的内容:
我也希望现有的图像也能被预先填充。数据将以如下形式提供:
{
"name":"Salted Butter",
"cost":"4",
"quantifier":"grams",
"servings":"250",
"quantity":"250",
"img":"data:image/jpeg;base64,/9j/4AAQSkZJRgABA...."
}
这是我到目前为止所拥有的:
ImageInput.jsx (组件传递到redux-form字段)
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 (将值预加载到表单中的页面)
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);