Реагирует на изменение состояния следующего компонента

#reactjs #react-dropzone

#reactjs #реагирует-dropzone

Вопрос:

Я должен создать уникальный компонент для загрузки файлов для ввода типа file, для этого я использую react-dropzone библиотеку с перетаскиванием и использую этот компонент 4 раза на странице. Начальный Первоначально, кроме первого компонента, другие компоненты должны быть отключены, но после загрузки файла следующий компонент должен изменить состояние на disable = {true}.Как мне правильно реализовать логику с помощью useState?

песочница

UploadFile.js

 const UploadFile = ({ visible }) => {
  const [active, setActive] = useState(false);

  useEffect(() => {
    if (!visible) {
      setActive(true);
    }
  }, [visible]);

  console.log(visible);

  const { getRootProps, getInputProps, open, acceptedFiles } = useDropzone({
    noClick: true,
    noKeyboard: true,
    disabled: active
  });

  const files = acceptedFiles.map((file) => (
    <li key={file.path}>
      {file.path} - {file.size} bytes
    </li>
  ));

  return (
    <>
      <div className="file-input">
        <div {...getRootProps({ className: "dropzone" })}>
          <input {...getInputProps()} />
          <p>Drag drop some files here</p>
          <button type="button" onClick={open} disabled={active}>
            Open File Dialog
          </button>
        </div>
        <aside>
          <h4>Files</h4>
          <ul>{files}</ul>
        </aside>
      </div>
    </>
  );
};
 

App.js

 export default function App() {
  return (
    <div className="App">
      <div className="upload-file">
        <h4>Please select file</h4>
        <div>
          <p>Images</p>
          <UploadFile visible />
          <UploadFile visible={false} />
        </div>
        <div>
          <p>Documents</p>
          <UploadFile visible={false} />
          <UploadFile visible={false} />
        </div>
        <br />
        <button type="submit" disabled={true}>
          SEND
        </button>
      </div>
    </div>
  );
}
 

Ответ №1:

вы можете использовать только одно состояние currentStep в качестве ссылки на своего родителя.

 import React, { useCallback, useState } from "react";
import UploadFile from "./UploadFile";
import "./styles.css";

export default function App() {
  const [currentStep, setCurrentStep] = useState(1)
  const nextStep = useCallback(() => {setCurrentStep(crr => crr   1)}, [])

  return (
    <div className="App">
      <div className="upload-file">
        <h4>Please select file</h4>
        <div>
          <p>Images</p>
          <UploadFile nextStep={nextStep} currentStep={currentStep} step={1} />
          <UploadFile nextStep={nextStep} currentStep={currentStep} step={2} />
        </div>
        <div>
          <p>Documents</p>
          <UploadFile nextStep={nextStep} currentStep={currentStep} step={3} />
          <UploadFile nextStep={nextStep} currentStep={currentStep} step={4} />
        </div>
        <br />
        <button type="submit" disabled={true}>
          SEND
        </button>
      </div>
    </div>
  );
}
 

при загрузке файла вам не нужен setState, только сравните step с currentStep, чтобы проверить, включен ли он. вам также необходимо проверить step === currentStep , как только удаление будет принято, иначе, если вы внесете несколько изменений в одно и то же поле, вы продолжите открывать другие поля.

 import React from "react";
import { useDropzone } from "react-dropzone";

const UploadFile = ({ nextStep, step, currentStep }) => {
  const onDropAccepted = () => {
        if(step === currentStep) nextStep()
  }

  const { getRootProps, getInputProps, open, acceptedFiles } = useDropzone({
    noClick: true,
    noKeyboard: true,
    disabled: step > currentStep,
    onDropAccepted
  });

  const files = acceptedFiles.map((file) => (
    <li key={file.path}>
      {file.path} - {file.size} bytes
    </li>
  ));

  return (
    <>
      <div className="file-input">
        <div {...getRootProps({ className: "dropzone" })}>
          <input {...getInputProps()} />
          <p>Drag drop some files here</p>
          <button type="button" onClick={open} disabled={step > currentStep}>
            Open File Dialog
          </button>
        </div>
        <aside>
          <h4>Files</h4>
          <ul>{files}</ul>
        </aside>
      </div>
    </>
  );
};

export default UploadFile;
 

Ответ №2:

вы можете использовать контекст реакции для создания взаимосвязи между каждым компонентом, и после первого удаления файла вы можете перейти к следующему индексу компонента загрузки

UploadFile.js

 import React, { useEffect, useState, useContext } from "react";
import { useDropzone } from "react-dropzone";
import { DragContext } from "./App";

const UploadFile = ({ index = 0 }) => {
  const { currentIndex, setCurrentIndex } = useContext(DragContext); // use context
  const [active, setActive] = useState(false);

  useEffect(() => {
    if (index !== currentIndex) { // compare to active 
      setActive(true);
    } else {
      setActive(false);
    }
  }, [currentIndex, index]);

  console.log(active);

  const { getRootProps, getInputProps, open, acceptedFiles } = useDropzone({
    noClick: true,
    noKeyboard: true,
    disabled: active,
    onDropAccepted: () => {
      setCurrentIndex(currentIndex   1);
    }
  });

  const files = acceptedFiles.map((file) => (
    <li key={file.path}>
      {file.path} - {file.size} bytes
    </li>
  ));

  return (
    <>
      <div className="file-input">
        <div {...getRootProps({ className: "dropzone" })}>
          <input {...getInputProps()} />
          <p>Drag drop some files here</p>
          <button type="button" onClick={open} disabled={active}>
            Open File Dialog
          </button>
        </div>
        <aside>
          <h4>Files</h4>
          <ul>{files}</ul>
        </aside>
      </div>
    </>
  );
};

export default UploadFile;

 

App.js

 import React, { createContext, useState } from "react";
import UploadFile from "./UploadFile";
import "./styles.css";

export const DragContext = createContext({});

export default function App() { 
  const [currentIndex, setCurrentIndex] = useState(0); // compare with index
  return (
    <div className="App">
      <DragContext.Provider value={{ currentIndex, setCurrentIndex }}>
        <div className="upload-file">
          <h4>Please select file</h4>
          <div>
            <p>Images</p>
            <UploadFile index={0} />
            <UploadFile index={1} />
          </div>
          <div>
            <p>Documents</p>
            <UploadFile index={2} />
            <UploadFile index={3} />
          </div>
          <br />
          <button type="submit" disabled={true}>
            SEND
          </button>
        </div>
      </DragContext.Provider>
    </div>
  );
}

 

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

1. Спасибо за работу с кодом, но как включить предыдущий ввод? потому что, если пользователь хочет загрузить другой файл и все загрузить кнопку включения файла?

2. вы можете сравнить currentIndex с индексом, если currentIndex> index и SetActive(false)