Общий совет с хуками apollo и слишком много повторных рендеров - PullRequest
1 голос
/ 08 мая 2020

фон: я пытаюсь выполнить загрузку файла с помощью DropZone в s3 и graphql, обслуживающего предварительно заданный URL-адрес для операций добавления и получения, и хотя он может быть не идеальным, он работает. Проблема, с которой я сейчас сталкиваюсь, заключается в том, что я добавляю в useMutation к pu sh результат в graphlql end, который записывает в базу данных mongodb. Я получаю слишком много повторных рендеров, поэтому ищу совета о том, как действительно понять, что здесь происходит. Каким бы уродливым ни был мой код, загрузка в s3 работает, если у меня нет addFileS3 (файл), addFileS3 (файл) вызывает useMutation в grpahql, чтобы записать результат в mongoDB, чтобы я мог получить файл позже, поэтому я предположил Лучшим местом для этого был ответ от ax ios.

const DropZone = ({ folderId, folderProps }) => {
  const [createS3File] = useMutation(ADD_FILE_S3);
  const addFileS3 = (file) => {
    createS3File({
      variables: {
        folderId: folderId,
        fileName: file.name,
      },
    })
      .then(({ data }) => {
        console.log("data", data);
      })
      .catch((e) => {
        console.log(e);
      });
  };
  const {
    acceptedFiles,
    getRootProps,
    getInputProps,
    isDragActive,
    isDragAccept,
    isDragReject,
  } = useDropzone({ accept: "image/*, application/pdf" });
  const [
    getPutURL,
    { loading: loading_url, error: error_url, data: data_url },
  ] = useLazyQuery(GET_S3_PUT_URL);
  if (loading_url) {
    console.log("loading");
  } else if (error_url) {
    console.log(error_url);
  } else if (data_url) {
    const results = data_url.PUTURL;
    results.map((file) => {
      const fileResult = acceptedFiles.filter(function(fileAcc) {
        return fileAcc.name === file.name;
      });

      const options = {
        params: {
          Key: file.name,
          ContentType: file.type,
        },
        headers: {
          "Content-Type": file.type,
        },
      };
      axios
        .put(file.url, fileResult[0], options)
        .then((res) => {
          //once i add the below here or outside axios post it goes mental on uploads
          addFileS3(file);
        })
        .catch((err) => {
        });
    });
  }

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

  const uploadDocs = () => {
    let files = [];
    acceptedFiles.map((file) => {
      const fileObj = { name: file.name, type: file.type };
      files.push(fileObj);
    });

    return getS3URLResult(files);
  };

  const getS3URLResult = async (files) => {
    getPutURL({
      variables: {
        packet: files,
      },
    });
  };



  return (
    <StyledDropZone>
      <div className="container">
        <Container
          {...getRootProps({ isDragActive, isDragAccept, isDragReject })}
        >
          <input {...getInputProps()} />
          <p>Drag 'n' drop some files here, or click to select files</p>
        </Container>
        {acceptedFilesItems}
      </div>
      <button onClick={() => uploadDocs(acceptedFiles)}>Upload</button>
    </StyledDropZone>
  );
};

1 Ответ

0 голосов
/ 08 мая 2020

Вы делаете запрос ax ios во время «потока» рендеринга, а не в обработчике / цепочке событий. Он вызывается, меняет состояние и вызывает следующую перерисовку - бесконечное l oop.

И мутация, и ленивый запрос имеют возможность использовать обработчик onCompleted. Это место для цепочки / вызова следующего действия (с использованием параметра data result).

... alse hanlder не должен ничего возвращать (return getS3URLResult(files);) - просто вызовите его (getS3URLResult(files);) или напрямую getPutURL.

update

Вероятно, вы ищете что-то вроде этого:

const DropZone = ({ folderId, folderProps }) => {
  const {
    acceptedFiles,
    getRootProps,
    getInputProps,
    isDragActive,
    isDragAccept,
    isDragReject,
  } = useDropzone({ accept: "image/*, application/pdf" });

  const uploadDocs = () => {
    let files = [];
    acceptedFiles.map((file) => {
      const fileObj = { name: file.name, type: file.type };
      files.push(fileObj);
    });
    console.log("uploadDocs, from acceptedFiles", files);
    // return getS3URLResult(files);
    getPutURL({
      variables: {
        packet: files,
      },
    });

  };

  const [
    getPutURL,
    { loading: loading_url, error: error_url, data: data_url },
  ] = useLazyQuery(GET_S3_PUT_URL, {
    onCompleted: (data) => {
      console.log("PUT_URL", data);

      const results = data.PUTURL;
      results.map((file) => {
        const fileResult = acceptedFiles.filter(function(fileAcc) {
          return fileAcc.name === file.name;
        });
        const options = {
          params: {
            Key: file.name,
            ContentType: file.type,
          },
          headers: {
            "Content-Type": file.type,
          },
        };
        axios
          .put(file.url, fileResult[0], options)
          .then((res) => {
            console.log("axios PUT", file.url);
            // addFileS3(file);
            createS3File({
              variables: {
                folderId: folderId,
                fileName: file.name,
              },
            })
          })
          .catch((err) => {
          });
      });

    }
  });

  const [createS3File] = useMutation(ADD_FILE_S3,{
    onCompleted: (data) => {
      console.log("ADD_FILE_S3", data);
      //setUploadedFiles( uploadedFiles,concat(data.somefilename) );
    }
  });                         

  const [uploadedFiles, setUploadedFiles] = useState( [] );


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

  const renderUploadedFiles ...

  return (
    <StyledDropZone>
      <div className="container">
        <Container
          {...getRootProps({ isDragActive, isDragAccept, isDragReject })}
        >
          <input {...getInputProps()} />
          <p>Drag 'n' drop some files here, or click to select files</p>
        </Container>
        {acceptedFilesItems}
        {uploadedFiles.length && <div class="success">
          {renderUploadedFiles}
        </div>}
      </div>
      <button onClick={() => uploadDocs(acceptedFiles)}>Upload</button>
    </StyledDropZone>
  );
};

Следует добавить некоторые оптимизации (useCallback), а не помещать их для ясности.

Для большей читабельности и оптимизации (лимит повторных рендеров) ... Я бы переместил почти всю (обработку) в отдельный подкомпонент - передал acceptedFiles в качестве опоры, отрисовал кнопку загрузки внутри.

...