首页 > 解决方案 > React- Button Onclick 不从文件系统渲染文件 - 在任何点击链接时它总是从文件对象渲染最后一个文件

问题描述

我对 React 比较陌生,当然在这个项目中我缺少一些基本的东西。

我在此处的代码和框中上传了一个具有此功能的小示例项目。虽然您会在代码和框中看到错误(因为目前不认为它支持 node 和 mongo 后端)。将代码推送到那里只是为了显示整个项目。

这是项目流程。

它是一个全栈项目,我在 /uploads 目录中使用 node 和 mongodb 将几个文件上传到项目的文件系统。但是,我当前的问题仅在 React 前端,即通过单击按钮呈现上传的文件。在 React 前端,我通过单击按钮然后打开 Material-UI 模式来呈现这些文件。因此,每次用户单击“下载文件”时,都会打开一个模态,在该模态中我单击“查看文件”按钮,相关文件应在新选项卡中呈现。但是每次我单击视图文件时,确实会渲染一个上传的文件,但它始终是“图像”对象中的最后一个文件。

这是我../src/Components/FileUpload.js实现的 Material-UI 模式。handleClose 函数接受一个参数fileLink,它是我从images对象中获取的文件的链接。

class FileUpload extends Component {
  state = {
    documents: [],
    open: false
  };

  handleClickOpen = () => {
    this.setState({ open: true });
  };

  handleClose = fileLink => {
    this.setState({
      open: false
    });
    window.open(`${fileLink}`, "_blank");
  };

  deleteDocument = id => {
    axios.delete("/api/document/" + id).then(() => {
      this.setState({
        documents: this.state.documents.filter(document => document._id !== id)
      });
    });
  };

  componentDidMount() {
    axios.get("/api/document").then(res => {
      this.setState({ documents: res.data });
    });
  }

  importAll = result => {
    let images = {};
    result.keys().map((item, index) => {
      return (images[item.replace("./", "")] = result(item));
    });
    return images;
  };

  render() {
    const webpackContext = require.context(
      "../../uploads",
      false,
      /\.(png|jpe?g|svg|pdf|doc|odt)$/
    );

    const images = this.importAll(webpackContext);
    console.log(images);

    return (
      <div className="bg-success">
        <Col xs="8">
          <Card>
            <CardHeader className="p-2 mb-2 bg-primary text-white" />
            <CardBody>
              <CardText>
                <table className="table table-stripe">
                  <thead>
                    <tr>
                      <th>Document Id</th>
                      <th>Description</th>
                    </tr>
                  </thead>
                  <tbody>
                    {this.state.documents.map(document => (
                      <tr>
                        <td>{document.document_id}</td>
                        <td>{document.description}</td>
                        <td>
                          <Button onClick={this.handleClickOpen}>
                            Download file
                          </Button>
                          <Dialog
                            open={this.state.open}
                            onClose={this.handleClose}
                            aria-labelledby="form-dialog-title"
                          >
                            <DialogTitle id="form-dialog-title">
                              Required Information
                            </DialogTitle>
                            <DialogContent>
                              <DialogContentText>
                                Update these info to download the file
                              </DialogContentText>
                              <TextField
                                autoFocus
                                margin="dense"
                                id="name"
                                label="Email Address"
                                type="email"
                                fullWidth
                              />
                            </DialogContent>
                            <DialogActions>
                              <Button
                                onClick={this.handleClose}
                                color="primary"
                              >
                                Cancel
                              </Button>
                              <Button
                                onClick={this.handleClose.bind(
                                  this,
                                  images[`${document.path}`]
                                )}
                                color="primary"
                              >
                                Download file
                              </Button>
                            </DialogActions>
                          </Dialog>
                        </td>
                        <td>
                          <Link
                            to={`/api/document/edit/${document._id}`}
                            class="btn btn-success"
                          >
                            Edit Description
                          </Link>
                        </td>
                        <td>
                          <button
                            onClick={this.deleteDocument.bind(
                              this,
                              document._id
                            )}
                            className="btn btn-danger"
                          >
                            Delete
                          </button>
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </CardText>
            </CardBody>
          </Card>
        </Col>

```

所以,点击按钮我正在做一个

onClick={this.handleClose.bind}handleClose将文件路径链接的参数传递给函数(即我应该能够呈现该文件的链接)

我正在使用 Webpack 的 require.context 从我在此组件中创建的对象中获取文件链接,并将其保存在名为images的变量中

const images = this.importAll(webpackContext);

但是每次我单击视图文件时,确实会渲染一个上传的文件,但它始终是“图像”对象中的最后一个文件。

handleClose函数不拾取图像对象中的其他文件。

标签: reactjsmaterial-ui

解决方案


回答我自己的问题(以防它帮助其他人) -

我犯的错误是这样的——

当将数据从我FileUpload.js .map的函数传递到handleClose传递 fileLink 进行渲染的函数时,我没有实现任何方法来挂钩到链接引用的那个特定文件。由 A) 从 FileUpload.js 单击按钮下载/查看文件时更正,现在我正在渲染一个全新的组件 ( RenderFile.js),它将负责打开 Material-UI 模式并渲染正确的链接以查看文件.

这是我更新的 FileUpload.js 文件

class FileUpload extends Component {
  state = {
    documents: [],
    clicked: false,
    textBeforeDownload: "View/Download"
  };


  launchRenderFileComp = () => {
    this.setState({
      clicked: true,
      textBeforeDownload: ""
    });
  };

  deleteDocument = id => {
    axios.delete("/api/document/" + id).then(() => {
      this.setState({
        documents: this.state.documents.filter(document => document._id !== id)
      });
    });
  };

  componentDidMount() {
    axios.get("/api/document").then(res => {
      this.setState({ documents: res.data });
    });
  }

  importAll = result => {
    let images = {};
    result.keys().map((item, index) => {
      return (images[item.replace("./", "")] = result(item));
    });
    return images;
  };

  render() {
    const { documents } = this.state;

    const webpackContext = require.context(
      "../../uploads",
      false,
      /\.(png|jpe?g|svg|pdf|doc|odt)$/
    );

    const images = this.importAll(webpackContext);
    return (
      <div className="bg-success">
        <Col xs="8">
          <Card>
            <CardHeader className="p-2 mb-2 bg-primary text-white" />
            <CardBody>
              <CardText>
                <table className="table table-stripe">
                  <thead>
                    <tr>
                      <th>Document Id</th>
                      <th>Description</th>
                    </tr>
                  </thead>
                  <tbody>
                    {documents.map(document => (
                      <tr>
                        <td>{document.document_id}</td>
                        <td>{document.description}</td>
                        <td>
                          <Button onClick={this.launchRenderFileComp}>
                            {this.state.clicked ? (
                              <RenderFile linkForRender={document.path} />
                            ) : null}
                            {this.state.textBeforeDownload}
                          </Button>
                        </td>
                        <td>
                          <Link
                            to={`/api/document/edit/${document._id}`}
                            class="btn btn-success"
                          >
                            Edit Description
                          </Link>
                        </td>
                        <td>
                          <button
                            onClick={this.deleteDocument.bind(
                              this,
                              document._id
                            )}
                            className="btn btn-danger"
                          >
                            Delete
                          </button>
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </CardText>
            </CardBody>
          </Card>
        </Col>

这是新的 RenderFile.js 组件,它将负责打开模式和渲染文件。

import React, { Component } from "react";
import Button from "@material-ui/core/Button";
import TextField from "@material-ui/core/TextField";
import Dialog from "@material-ui/core/Dialog";
import DialogActions from "@material-ui/core/DialogActions";
import DialogContent from "@material-ui/core/DialogContent";
import DialogContentText from "@material-ui/core/DialogContentText";
import DialogTitle from "@material-ui/core/DialogTitle";

class RenderFile extends Component {
  state = {
    open: false
  };

  handleToggle = () => {
    this.setState({
      open: !this.state.open
    });
  };

  handleClickOpen = () => {
    this.setState({ open: true });
  };

  handleClose = file => {
    // file = this.file;
    this.setState({
      open: false
    });
    window.open(`${file}`, "_blank");
  };

  handleCancel = () => {
    this.setState({ open: false });
  };

  importAll = result => {
    let images = {};
    result.keys().map((item, index) => {
      return (images[item.replace("./", "")] = result(item));
    });
    return images;
  };

  render() {
    const webpackContext = require.context(
      "../../uploads",
      false,
      /\.(png|jpe?g|svg|pdf|doc|odt)$/
    );

    const images = this.importAll(webpackContext);
    const { linkForRender } = this.props;

    return (
      <div>
        <Button onClick={this.handleClickOpen}>Click to View File</Button>
        <Dialog
          open={this.state.open}
          onClose={this.handleToggle}
          aria-labelledby="form-dialog-title"
        >
          <DialogTitle id="form-dialog-title">Required Information</DialogTitle>
          <DialogContent>
            <DialogContentText>
              Update these info to download the file
            </DialogContentText>
            <TextField
              autoFocus
              margin="dense"
              id="name"
              label="Email Address"
              type="email"
              fullWidth
            />
          </DialogContent>
          <DialogActions>
            <Button onClick={this.handleCancel} color="primary">
              Cancel
            </Button>
            <Button
              onClick={this.handleClose.bind(this, images[`${linkForRender}`])}
              color="primary"
            >
              View or Download File
            </Button>
          </DialogActions>
        </Dialog>
      </div>
    );
  }
}

推荐阅读