我已经围绕react-dropzone构建了一个包装器:
Dropzone.jsx
import React, { useState, useCallback } from "react";
import PropTypes from "prop-types";
import classNames from "classnames";
import { useDropzone } from "react-dropzone";
import { noop } from "../utils";
const Dropzone = ({
id,
name,
label,
className,
touched,
error,
showError,
children,
onDrop,
showUploadedFiles,
onChange,
onRemoveFile // ...rest
}) => {
const [uploadedFiles, setUploadedFiles] = useState([]);
const _onChange = value => {
onChange();
console.log(">>>>>", "onchange triggerd", value);
setUploadedFiles(prevValue => [
...prevValue.map(fileObj => {
if (fileObj.file.name === value.filename) {
return {
id: value.id,
file: fileObj.file
};
}
return fileObj;
})
]);
};
const _onDrop = useCallback(
acceptedFiles => {
setUploadedFiles(prevValue => [
...prevValue,
...acceptedFiles.map(file => ({
id: "PLACEHOLDER",
file
}))
]);
onDrop(acceptedFiles);
},
[onDrop]
);
const { getRootProps, getInputProps, isDragActive } = useDropzone({
onDrop: _onDrop
});
return (
// eslint-disable-next-line jsx-a11y/label-has-for
<label htmlFor={id || name}>
<span className="label-text">{label}</span>
{showUploadedFiles && (
<div className="uploaded-files">
{uploadedFiles.length > 0
? uploadedFiles.map(fileObj => (
<div className="uploaded-file">
<span className="file-name">{fileObj.file.name}</span>
</div>
))
: "No files uploaded"}
</div>
)}
<div
{...getRootProps()}
onChange={_onChange}
className="dropzone-container"
>
<input {...getInputProps()} />
<div className={classNames("dropzone", className)}>
{children ? (
children(isDragActive)
) : (
<div className="dropzone-content">
<span>Upload</span>
</div>
)}
</div>
</div>
{showError && touched && error.message && (
<span className="error">{error.message}</span>
)}
</label>
);
};
Dropzone.propTypes = {
id: PropTypes.string,
name: PropTypes.string.isRequired,
placeholder: PropTypes.string,
label: PropTypes.string,
control: PropTypes.instanceOf(Object).isRequired,
className: PropTypes.string,
popperClassName: PropTypes.string,
touched: PropTypes.bool,
error: PropTypes.instanceOf(Object),
showError: PropTypes.bool,
children: PropTypes.func,
onDrop: PropTypes.func,
showUploadedFiles: PropTypes.bool,
onChange: PropTypes.func,
onRemoveFile: PropTypes.func
};
Dropzone.defaultProps = {
id: "",
placeholder: "",
label: "",
className: "",
popperClassName: "",
touched: false,
error: {},
showError: false,
children: null,
onDrop: noop,
showUploadedFiles: false,
onChange: noop,
onRemoveFile: noop
};
export default Dropzone;
以及 Dropzone.jsx 的包装器,以便将其与 react-hook-form
一起使用。
DropzoneWrapper.jsx
import React from "react";
import PropTypes from "prop-types";
import { Controller } from "react-hook-form";
import Dropzone from "./Dropzone";
const DropzoneWrapper = ({ control, ...rest }) => (
<Controller as={Dropzone} control={control} showError {...rest} />
);
DropzoneWrapper.propTypes = {
control: PropTypes.instanceOf(Object).isRequired
};
export default DropzoneWrapper;
我还为 Form 构建了一个新组件。但我不会详细介绍它,因为没有必要在这里展示它来解决这个问题。但我已经建立了一个codesandbox ,其中我们有一个名为 Form.jsx 的文件
在App.jsx中,当我单击“Click Me”按钮时,我会在 Dropzone 上调用 setValue。
import React from "react";
import "./styles.css";
import { useForm } from "react-hook-form";
import Form from "./controls/Form";
import DropzoneWrapper from "./controls/DropzoneWrapper";
export default function App() {
const formMethods = useForm({});
const { setValue } = formMethods;
const onButtonClick = () => {
setValue("example", { filename: "test", id: "1234" });
};
return (
<div className="App">
<Form methods={formMethods}>
<DropzoneWrapper field name="example" showUploadedFiles />
<button onClick={onButtonClick} type="button">
Click Me
</button>
</Form>
</div>
);
}
我不知道如何将它与 Dropzone.jsx 的 onChange 函数连接起来,因此当我执行 setValue('example', Something) 时,会调用 Dropzone.jsx 的 _onChange 内的 console.log 。
如果您错过了,这里又是codesandbox 链接:
更新
<小时/>@Sabit Rakhim 感谢您的辛勤工作和时间。但不幸的是这不是我的本意。此处按钮单击只是一个示例。
- 实际上,当选择文件时,onDrop 函数将会被调用。
- 我将在 onDrop 函数主体中触发 redux 操作,该操作将调用 API 来上传文件。
- 上传文件后,服务器将返回我的uuid。
- 现在,我将在 App.jsx 文件中获取该 uuid,然后通过执行 setValue 将其发送到 dropzone 组件。
现在,如果我的组件(在本例中为 dropzone)的 onChange 方法被 App.jsx 中的 setValue 调用,那么我会将上传文件的 id 从 PLACEHOLDER 替换为实际 id。
现在,当我提交表单时,我应该在表单值中获取这些文件,这些文件作为参数传递给提交函数。
这就是整个流程。
最佳答案
也许不是最好的方式)
降落区
const Dropzone = ({
id,
name,
label,
className,
touched,
error,
showError,
children,
onDrop,
showUploadedFiles,
onChange,
isClicked, // get value from props
setClicked,
onRemoveFile // ...rest
}) => {
const [uploadedFiles, setUploadedFiles] = useState([]);
const _onChange = value => {
console.log(">>>>>", "onchange triggerd", value);
onChange();
setUploadedFiles(prevValue => [
...prevValue.map(fileObj => {
if (fileObj.file.name === value.filename) {
return {
id: value.id,
file: fileObj.file
};
}
return fileObj;
})
]);
setClicked(false); // you can remove it if you sure that users click only 1 time
};
React.useEffect(() => { //run _onChange if click event change, you can add conditions there
_onChange();
}, [isClicked]);
App.js
export default function App() {
const formMethods = useForm({});
const [isClicked, setClicked] = React.useState(false); //added bool to check status
const { setValue } = formMethods;
const onButtonClick = () => {
setValue("example", { filename: "test", id: "1234" });
setClicked(true);
};
return (
<div className="App">
<Form methods={formMethods}>
<DropzoneWrapper
field
name="example"
showUploadedFiles
isClicked={isClicked}
setClicked={setClicked}
/>
<button onClick={onButtonClick} type="button">
Click Me
</button>
</Form>
</div>
);
}
关于reactjs - 当调用 setValue 时触发组件内部定义的 onChange,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61798129/