【问题标题】:The second time onChange is triggered, custom component does not update第二次触发onChange,自定义组件不更新
【发布时间】:2021-12-09 07:22:25
【问题描述】:

问题:props.files 被控制台记录时(查看下面的代码以了解它的记录位置),它第一次工作。但是,当我选择一组新文件时,它永远不会再次记录任何内容,除非我重新加载页面并重新开始。我在这里做错了什么?

我的代码信息:

我正在为以下自定义组件(即 Form.Group、Form.Label、Form.Control)使用React Bootstrap Form Components(滚动到“文件输入”)。为了清晰起见,代码被大大简化:

const UploadFile = (props) => {
  // stuff here

  console.log(props.files);

  const filesFunction = (filesInput) {
     // do stuff with filesInput
  }
  
  return {
     <Form.Group controlId="formFileMultiple" className="mb-3">
        <Form.Label> Files </Form.Label>
        <Form.Control 
           type="file" multiple 
           onChange={props.handleFilesChange}
           onChange={filesFunction(files)}
           name="files"
           key="files"
        />
     </Form.Group>
  }

在父级中,我有一个自定义 UploadFile 组件的实例,如下所示:

const [listOfFiles, setListOfFiles] = useState([]);

const handleFilesChange = (e) {
   setListOfFiles(e.target.files);
}

<UploadFile 
   handleFilesChange = { e => handleFilesChange(e) }
   files = {listOfFiles}
/>

【问题讨论】:

  • 如果handleFilesChange 不起作用,您应该在问题中包含该代码。除了与问题实际相关的内容之外,您几乎向我们展示了所有内容。
  • handleFilesChange 在代码中,但仅在父代码中!这可能是它不起作用的原因
  • @thomas,我将它包含在 onChange 中。它不会那样触发吗?
  • @Iwrestledabearonce。这就是我所拥有的handleFilesChanges。你还想看什么?
  • @penguin 它应该被触发,我不明白为什么它不应该......当你将控制台日志放在handleFilesChange 函数中时会发生什么?每次上传新文件时都会显示吗?

标签: javascript reactjs react-hooks onchange custom-component


【解决方案1】:

编辑:如果要在 UploadFile 组件中触发 console.log,则必须触发重新渲染。目前我没有看到你在 UploadFile 组件的渲染方法中使用props.files。也许试试这个:

const UploadFile = (props) => {
  // stuff here

  console.log(props.files);
  const randomClassName = `random-class-name-${props.files.length}`;

  return {
     <Form.Group controlId="formFileMultiple" className="mb-3">
        <Form.Label className={randomClassName}> Files </Form.Label>
        <Form.Control 
           type="file" multiple 
           onChange={props.handleFilesChange}
           name="files"
           key="files"
        />
     </Form.Group>
  }
}

但正确的做法是使用 useEffect 钩子,每次变量独立于渲染方法发生变化时都会触发该钩子。这是一个例子:

import { useEffect } from "react";

const UploadFile = (props) => {
  
  useEffect(() => {
    // stuff here
    console.log(props.files);
  }, [props.files]);
  
  return {
     <Form.Group controlId="formFileMultiple" className="mb-3">
        <Form.Label> Files </Form.Label>
        <Form.Control 
           type="file" multiple 
           onChange={props.handleFilesChange}
           name="files"
           key="files"
        />
     </Form.Group>
  }
}

【讨论】:

  • 刚试过这个。不幸的是,它不起作用。得到了和以前一样的结果。
  • 更新了答案,不确定是否对你有帮助,但你可以试试这个^^
  • 嗨@thomas。我还没有听说过 useEffects,所以我会对此做更多的研究。感谢那!但是,我尝试了你写的,我仍然再次得到原始结果:(
【解决方案2】:

React 不知道如何管理 FileList 并且在子组件中看不到这些 props 的变化。如果您将FileList 转换为array,它会完美运行。

const UploadFile = (props) => {
  // stuff here

  console.log(props.files);
  
  return <ReactBootstrap.Form.Group controlId="formFileMultiple" className="mb-3">
      <ReactBootstrap.Form.Label> Files </ReactBootstrap.Form.Label>
      <ReactBootstrap.Form.Control 
         type="file" multiple 
         onChange={props.handleFilesChange}
         name="files"
         key="files"
      />
   </ReactBootstrap.Form.Group>
}

function App() {
  const [listOfFiles, setListOfFiles] = React.useState([]);

  const handleFilesChange = (e) => {
     setListOfFiles(Array.from(e.target.files));
  }

  return <UploadFile 
     handleFilesChange = { e => handleFilesChange(e) }
     files = {listOfFiles}
  />
}

ReactDOM.render(
  <App />, 
  document.getElementById('root')
)
<script src="https://unpkg.com/react/umd/react.production.min.js" crossorigin></script>

<script
  src="https://unpkg.com/react-dom/umd/react-dom.production.min.js"
  crossorigin></script>

<script
  src="https://unpkg.com/react-bootstrap@next/dist/react-bootstrap.min.js"
  crossorigin></script>

<div id="root"></div>

澄清:

FileList 不受 react 管理,因为它的值只能由用户设置,不能以编程方式设置:https://reactjs.org/docs/uncontrolled-components.html#the-file-input-tag

【讨论】:

  • 谢谢。但是,我需要将类型保留为 FileList,因为“文件”将作为输入进入的服务需要它。好像没有办法把array转回FileList,还是有?
  • 没有@penguin。您需要将FileListarray 保存为道具。
  • 感谢您的帮助!事实证明,该服务使用了这两种类型(FileList 和数组)并且它工作得很好,所以不需要两个道具。我还有另一个小问题——如果我想在 UploadFile 中使用“文件”作为函数的输入,在用户选择文件后,我该怎么做?我已经更新了上面的代码以包含函数(filesFunction)和 onChange(添加了第二个 onChange),但这不起作用,因为我们不能同时拥有两个 onChanges。
  • 如果我理解正确:onChange={(e) =&gt; {props.handleFilesChange(e); otherFunction(e.target.files) }}handleFilesChange 更好
猜你喜欢
  • 2023-04-02
  • 2013-11-07
  • 1970-01-01
  • 1970-01-01
  • 2021-10-16
  • 2018-02-27
  • 2017-07-19
  • 2023-03-17
  • 2019-09-21
相关资源
最近更新 更多