reactjs - 当调用 setValue 时触发组件内部定义的 onChange

标签 reactjs react-hook-form

我已经围绕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 链接:

Edit purple-brook-nxpm7

更新

<小时/>

@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/

相关文章:

javascript - 在reactjs中从父组件调用并将参数传递给子组件的函数

reactjs - 为什么编辑器字段不以 react-hook-form 呈现最近的数据

javascript - react Hook 形式 setValue 返回未定义的多选( react 选择)

javascript - 如何捕获 react 钩子(Hook)表单验证错误

react-hook-form - useWatch 和 useForm 的 watch 有什么区别? [ react Hook 形式]

javascript - js 中函数的上下文可见性 (React.js)

javascript - React子组件props : _this2. props.delete它不是一个函数

javascript - 如何避免被大量的框架和新版本的 JavaScript 淹没?

reactjs - react-hook-form:表单状态和输入状态之间双向映射的惯用方法

reactjs - 为什么我的组件的基于 redux 的 Prop 没有更新?