首页 > 解决方案 > 推送新对象时未定义状态值

问题描述

我正在尝试将对象推{link:href}送到我的项目中的状态。当我尝试在其中添加 href 时{link:href}说它是未定义的:

图片

这是我的代码:

class Download extends React.Component {
  constructor(props) {
    super(props);

    this.handleClick = this.handleClick.bind(this);

    this.inputRef = React.createRef();

    this.state = {
      files: []
    };
  }

  handleClick = () => {
    const node = this.inputRef.current;

    const self = this;

    let file;
    let name;
    let href;

    node.addEventListener("change", function() {
      const fileList = [];

      for (let x = 0, xlen = this.files.length; x < xlen; x++) {
        file = this.files[x];
        name = file.name;

        fileList.push({ name: name });
        let reader = new FileReader();
        reader.onload = e => {
          href = e.target.result;
        };

        fileList.push({ link: href });
        reader.readAsDataURL(file);
      }

      self.setState({ files: fileList });
      console.log(self.state);
    });
  };

  render() {
    return (
      <div className="input">
        <input
          onClick={this.handleClick}
          id="upload-file"
          className="inputName"
          type="file"
          multiple
          ref={this.inputRef}
        />
        <div>
          <ul ref={this.ulRef}>
            {this.state.files.map((file, index) => (
              <li key={index}>
                <Link to={file.link}>{file.name}</Link>
              </li>
            ))}
          </ul>
        </div>
      </div>
    );
  }
}

export default Download;

我试图{link:href}在 reader.onload 函数之后推送的原因是因为 reader.onload 函数在 for 循环运行 x 次后被加载并且它只显示最后一项。

标签: javascriptreactjs

解决方案


首先,你不需要为此使用任何东西refs。而且您不需要向node(根本不需要node)添加事件侦听器,因为您可以使用 react'sonChange而不是onClick. 您也不需要为此使用 a Link。您可以只使用带有 href 的锚标签,您可以将文件数据分配给该标签。我更喜欢使用处理下载的函数,因为我觉得它给了我更多的控制权,并且不会影响浏览器的窗口或 DOM 元素。您实际上是创建一个锚标记,触发下载,然后将其删除。

您还错误地推入了元素。通过推{name: filename}然后推入,{link: href}您刚刚将两个对象推入您的数组,并且这两个对象都将被映射。相反,创建一个对象,并将这两个键值对添加到该对象并将该单个对象推入。在到达reader.onload. 通过在 the 之前推一些东西,reader.onload然后再在里面reader.onload推一些东西,你只是把东西推了两次。

添加name到对象。删除第一次推送,然后reader.onloadlink值添加到同一个对象,然后将对象推送到数组中。您也不应该在 之外设置状态,reader.onload因为这会触发重新渲染并渲染您不完整的对象,从而导致错误。仅在 中设置一次状态,reader.onload以便在触发重新渲染时它将在列表中拥有完整的对象。

如果您希望您的文件持久存在(意味着允许将多个文件添加到您的文件列表中),而不是fileList每次onChange触发时设置为空数组,请将其设置为this.state.files.slice(). 我们可以使用sliceas 创建一个新数组而不是this.state.files直接变异。

这是一个工作代码沙箱

正如您将看到的,我也删除了您的constructoras 对于这个用例,只需使用state = {...}就足够了。如果您更喜欢使用,constructor您可以根据需要将其添加回来,我只是自己承担了为您节省几行代码的责任。

class Download extends React.Component {

  state = {
    files: []
  };

  handleClick = event => {
    let file, name, href;

    var breh = event.target;

    const fileList = this.state.files.slice();

    for (let x = 0, xlen = breh.files.length; x < xlen; x++) {
      file = breh.files[x];
      name = file.name;

      var obj = {};
      obj["name"] = name;

      let reader = new FileReader();
      reader.onload = e => {
        href = e.target.result;
        obj["link"] = href;
        fileList.push(obj);
        this.setState({ files: fileList });
      };

      reader.readAsDataURL(file);
    }
  };

  download = (uri, name) => {
    var link = document.createElement("a");
    link.download = name;
    link.href = uri;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
  };

  render() {
    console.log(this.state);
    return (
      <div className="input">
        <input
          onChange={this.handleClick}
          id="upload-file"
          className="inputName"
          type="file"
          multiple
        />
        <div>
          <ul>
            {this.state.files.map((file, index) => {
              return (
                <li
                  key={index}
                  onClick={() => {
                    this.download(file.link, file.name);
                  }}
                >
                  {file.name}
                </li>
              );
            })}
          </ul>
        </div>
        <output id="list" />
      </div>
    );
  }
}

export default Download;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>


推荐阅读