При втором запуске onChange пользовательский компонент не обновляется

#javascript #reactjs #react-hooks #onchange #custom-component

Вопрос:

Вопрос: Когда props.files консоль регистрируется (посмотрите код ниже, чтобы увидеть, где он регистрируется), он работает в первый раз. Но, когда я выбираю новый набор файлов, консоль больше никогда ничего не регистрирует, если я не перезагружу страницу и не начну все сначала. Что я здесь делаю не так?

Информация о моем коде:

Я использую компоненты формы React Bootstrap (прокрутите до «Ввод файла») для следующего пользовательского компонента (т.е. Form.Group, Form.Label, Form.Control). Код сильно упрощен для наглядности:

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

Комментарии:

1. если handleFilesChange не работает, вы должны включить этот код в вопрос. вы показали нам практически все, кроме того, что действительно имеет отношение к проблеме.

2. handleFilesChange есть в коде, но только в родительском! Это может быть причиной, по которой он не работает

3. @thomas, я включил его в onChange. Разве это не срабатывает таким образом?

4. @Iwrestledabearonce. Это все, что у меня есть для обработки изменений файлов. Что еще вы хотели бы увидеть?

5. @penguin это должно быть запущено, я не понимаю, почему этого не должно быть… что происходит, когда вы помещаете журнал консоли внутри handleFilesChange функции? Отображается ли он каждый раз, когда вы загружаете новые файлы?

Ответ №1:

РЕДАКТИРОВАТЬ: Если вы хотите console.log , чтобы он запускался в компоненте uploadFile, вам необходимо запустить повторный запуск. В настоящее время я не вижу, чтобы вы использовали props.files метод рендеринга внутри компонента uploadFile. Может быть, попробуйте это:

 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>
  }
}
 

Комментарии:

1. Просто попробовал это. К сожалению, это не работает. Получил тот же результат, что и раньше.

2. обновил ответ, не уверен, поможет ли это вам, но вы можете попробовать это ^^

3. Привет @thomas. Я не слышал об эффектах использования, поэтому я буду проводить дополнительные исследования по этому вопросу. Спасибо за это! Но я попробовал то, что вы написали, и я все еще получаю исходный результат снова: (

Ответ №2:

React не знает, как управлять FileList и не видит эти изменения реквизитов в дочернем компоненте. Если вы конвертируете 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> 

Уточнение:

Список файлов не управляется react, поскольку его значение может быть установлено только пользователем, а не программно: https://reactjs.org/docs/uncontrolled-components.html#the-file-input-tag

Комментарии:

1. Спасибо. Однако мне нужно сохранить тип как список файлов, потому что это требуется для службы, в которую «файлы» будут вводиться в качестве входных данных. Кажется, что нет способа преобразовать массив обратно в список файлов или есть?

2. Нет @penguin. Вам нужно будет сохранить FileList и array как реквизит.

3. Спасибо за вашу помощь! Оказывается, сервис использует оба типа (список файлов и массив) и работает отлично, поэтому нет необходимости в двух реквизитах. У меня также возник еще один быстрый вопрос — если бы я хотел использовать «files» в качестве входных данных для функции в uploadFile, после того, как пользователь выберет файлы, как бы я это сделал? Я обновил приведенный выше код, чтобы включить функцию (filesFunction) и onChange (добавил второй onChange), но это не работает, так как у нас не может быть двух onChanges одновременно.

4. если я правильно понимаю: onChange={(e) => {props.handleFilesChange(e); otherFunction(e.target.files) }} или лучше в handleFilesChange