【问题标题】:RefluxJS "single" store?RefluxJS“单一”商店?
【发布时间】:2016-01-02 17:38:12
【问题描述】:

嗯.. 我创建了一个上传组件,当用户上传图像时,该组件使用 FileReader API 显示图像预览。

但是,如果我一直在另一个组件中使用 3 个组件,当我上传一张图片时,这张图片也会在 3 个组件中重复。

例子:

... in render method
<UploadImage />
<UploadImage />
<UploadImage />
.... 

我的组件:

var React = require('react');
var Reflux = require('reflux');

// Actions
var actions = require('../../actions/Actions');

// Stores
var UploadStore = require('../../stores/ui/UploadStore');

var UI = require('material-ui');
var FlatButton = UI.FlatButton;
var Snackbar = UI.Snackbar;

var UploadImage = React.createClass({

  mixins: [Reflux.connect(UploadStore, 'upload')],

  propTypes: {
    filename: React.PropTypes.string,
    filesrc: React.PropTypes.string,
    extensions: React.PropTypes.array.isRequired
  },

  getDefaultProps: function() {
    return {
      extensions: ['jpg', 'png', 'jpeg', 'gif']
    };
  },

  _uploadImage: function () {
    var file = {
      file: this.refs.upload.getDOMNode().files[0] || false,
      extensions: this.props.extensions
    };

    try {
      actions.upload(file);
    }
    catch (e) {
      console.log(e);
    }
  },


  _uploadedImage: function() {
    if (this.state.upload.filename) {
      return (
        <div className="upload-image">
          <img src={this.state.upload.filesrc} />
          <p>{this.state.upload.filename}</p>
        </div>
      );
    }
  },

  render: function() {

    return (
      <div className="upload-image-container component-container">
        <div className="upload-fields component-fields">
          <h3>Imagem</h3>
          <p>Arquivos PNG ou SVG no tamanho de XXXxYYYpx de até 50kb.</p>

          <FlatButton label="Selecionar Imagem" className="upload-button">
            <input
              type="file"
              id="imageButton"
              className="upload-input"
              ref="upload"
              onChange={this._uploadImage} />
          </FlatButton>
        </div>

        {this._uploadedImage()}
      </div>
    );
  }
});

module.exports = UploadImage;

我的商店:

var Reflux = require('reflux');

var actions = require('../../actions/Actions');

var UploadStore = Reflux.createStore({

  listenables: [actions],

  data: {
    filename: '',
    filesrc: ''
  },

  getInitialState: function() {
    return this.data;
  },

  onUpload: function (f) {
    if (f) {
      // Check extension
      var extsAllowed = f.extensions;

      if (this.checkExtension(extsAllowed, f.file.name)) {

        // Crate the FileReader for upload
        var reader = new FileReader();
        reader.readAsDataURL(f.file);

        reader.addEventListener('loadend', function() {
          this.setData({
            uploaded: true,
            filename: f.file.name,
            filesrc: reader.result
          });
        }.bind(this));

        reader.addEventListener('error', function () {
          actions.error('Não foi possível ler o seu arquivo. Por favor, verifique se enviou o arquivo corretamente.');
        }.bind(this));
      }
      else {
        actions.error('O arquivo que você está tentando enviar não é válido. Envie um arquivo nas seguintes extensões: ' + extsAllowed.join(', ') + '.');
      }
    }
    else {
      actions.error('File object not found.');
    }
  },

  checkExtension: function (extensions, filename) {
    var fileExt = filename.split('.').pop().toLowerCase();
    var isSuccess = extensions.indexOf(fileExt) > -1;

    if (isSuccess) return true;

    return false;
  },

  setData: function(data) {
    this.data = data;

    this.trigger(data);
  }

});

module.exports = UploadStore;

结果:

有什么想法吗?

谢谢!

【问题讨论】:

    标签: javascript reactjs refluxjs


    【解决方案1】:

    不幸的是,商店的行为就像一个单例,即只有一个 UploadStore 实例。

    您可以做的是引入一个额外的参数来保持上传分开。您的商店现在将进行一系列上传,但每次上传都带有类别标签,您的组件也将有一个类别,并且仅从商店中获取属于同一类别的图像。这是使用Reflux.connectFilter mixin 完成的。

    首先,我将上传的图片分成自己的组件,如下所示:

    var UploadedImage = React.createClass({
      propTypes: {
        upload: React.PropTypes.object.isRequired
      },
    
      render: function() {
          return (
            <div className="upload-image">
              <img src={this.props.upload.filesrc} />
              <p>{this.props.upload.filename}</p>
            </div>
          );
      }
    });
    

    然后我们必须更改 UploadImage 组件中的一些内容,以便它按类别过滤:

    var UploadImage = React.createClass({
    
      // only select those uploads which belong to us
      mixins: [
        Reflux.connectFilter(UploadStore, "uploads", function(uploads) {
            return uploads.filter(function(upload) {
               return upload.category === this.props.category;
            }.bind(this))[0];
        })
      ],
    
      propTypes: {
        filename: React.PropTypes.string,
        filesrc: React.PropTypes.string,
        extensions: React.PropTypes.array.isRequired,
        // an additional prop for the category
        category: React.PropTypes.string.isRequired
      },
    
      _uploadImage: function () {
        var file = {
          file: this.refs.upload.getDOMNode().files[0] || false,
          extensions: this.props.extensions
        };
    
        try {
          // pass in additional parameter!
          actions.upload(file, this.props.category);
        }
        catch (e) {
          console.log(e);
        }
      },
    
      render: function() {
        return (
          <div className="upload-image-container component-container">
            <div className="upload-fields component-fields">
              <h3>Imagem</h3>
              <p>Arquivos PNG ou SVG no tamanho de XXXxYYYpx de até 50kb.</p>
    
              <FlatButton label="Selecionar Imagem" className="upload-button">
                <input
                  type="file"
                  id="imageButton"
                  className="upload-input"
                  ref="upload"
                  onChange={this._uploadImage} />
              </FlatButton>
            </div>
    
            {this.state.uploads.map(function(upload, index) {
               return <UploadedImage key={index} upload={upload}/>;
            })}
          </div>
        );
      }
    });
    

    您的商店现在拥有一个“文件”对象数组,每个对象都标有一个类别:

    var UploadStore = Reflux.createStore({
    
      listenables: [actions],
    
      // data is now an array of objects
      data: [],
    
      getInitialState: function() {
        return this.data;
      },
    
      // here we get the file + category
      onUpload: function (f, category) {
        if (f) {
          // Check extension
          var extsAllowed = f.extensions;
    
          if (this.checkExtension(extsAllowed, f.file.name)) {
    
            // Crate the FileReader for upload
            var reader = new FileReader();
            reader.readAsDataURL(f.file);
    
            reader.addEventListener('loadend', function() {
              this.setData(this.data.concat([{
                uploaded: true,
                filename: f.file.name,
                filesrc: reader.result,
                category: category /* adding category here */
              }]));
            }.bind(this));
    
            reader.addEventListener('error', function () {
              actions.error('Não foi possível ler o seu arquivo. Por favor, verifique se enviou o arquivo corretamente.');
            }.bind(this));
          }
          else {
            actions.error('O arquivo que você está tentando enviar não é válido. Envie um arquivo nas seguintes extensões: ' + extsAllowed.join(', ') + '.');
          }
        }
        else {
          actions.error('File object not found.');
        }
      },
    
      checkExtension: function (extensions, filename) {
        var fileExt = filename.split('.').pop().toLowerCase();
        var isSuccess = extensions.indexOf(fileExt) > -1;
    
        if (isSuccess) return true;
    
        return false;
      },
    
      setData: function(data) {
        this.data = data;
    
        this.trigger(data);
      }
    
    });
    

    最后在您看来,您可以像这样使用UploadImage 组件:

    我是即时编写代码的,因此可能存在一些问题 - 但更多的是关于概念。此外,现在每个类别可以上传多个图像,如果不需要,那么考虑用哈希映射替换商店中的数组,以便键对应于类别 - 然后只能上传一张图像每个类别。

    回答您的评论

    也许你可以使用 store 的工厂方法,例如:

    var UploadStoreFactory = function() {
      return Reflux.createStore({
        /* your existing code as it was originally */
      });
    };
    
    var UploadImage = React.createClass({ 
      mixins: [Reflux.connect(UploadStoreFactory(), 'upload')],
    
      /* your existing code as it was originally */
    });
    

    但我怀疑您的操作会触发您上传存储的所有实例,但值得一试。但这带来了很多缺点,例如其他组件无法轻松收听此商店。

    thisstackoverflow 中提出了一个类似的问题,并且概念上正确的方法是对所有人使用一个存储桶/存储,并将存储中的项目标记,以便您可以将它们分开。

    请记住,商店也会被重新填充,例如,如果您创建一个包含产品和不同类别的网上商店,则每次用户切换到另一个类别时,您都会清除并重新填充 ProductStore。如果您还有一个可能显示“您可能喜欢的产品”的侧边栏,那么我会将其建模为一个单独的商店,即ProductSuggestionStore,但两者都包含“产品”类型的对象。

    如果商店在语义上表现不同但共享大量上传逻辑,您也可以尝试为商店构建基本原型/类,然后扩展特定商店或将上传逻辑外包到服务类中。

    如果您担心性能问题,即一次上传会导致所有组件重新渲染,那么您可以在shouldComponentUpdate 中添加检查。

    为什么只使用一个商店的一个很好的例子可能是用户想要关闭窗口但在您网站的某个地方上传仍然挂起,然后您的主应用程序视图只需要检查一个商店。上传也可以很容易地排队,这样带宽就不会耗尽,因为所有上传都通过一个商店。

    另外请记住,您可以拥有监听其他商店的商店,例如,UploadHistoryStore 保留最近 10 次上传的时间戳记录。所有上传都进入同一个存储桶,但如果您有“最近 10 次上传”组件,它只需要收听“UploadHistoryStore”

    var UploadStore = Reflux.createStore({
        /* ... upload stuff and trigger as usual ... */
    });
    
    
    var UploadHistoryStore = Reflux.createStore({
        // keep the last ten uploads
        historyLength: 10,
    
        init: function() {
            // Register statusStore's changes
            this.listenTo(UploadStore, this.output);
            this.history = [];
        },
    
       // Callback
        output: function(upload) {
            this.history.push({
                date: new Date(),  // add a date when it was uploaded
                upload: upload     // the upload object
            }).slice(1, this.historyLength);
    
            // Pass the data on to listeners
            this.trigger(this.history);
        }
    });
    

    【讨论】:

    • 谢谢@sled!这是唯一的方法吗?如果我有其他具有类似行为的组件,我总是需要这样做吗?在 Flux 中,我也这样做?
    • 在不断变化的情况下,它会是一样的 - 我添加了一个示例,如何创建监听其他商店的商店并只获取其中的一个子集。
    • 再次感谢@sled!我正在使用第一个选项,与 connectFilter 一起使用。问题是我的许多组件都是可重用的并且使用单个存储,代码可能会更长,因为我必须在几乎所有东西中使用过滤器。在使用 Reflux 之前,我没有遇到这个问题,但是我的代码非常投入,我不得不保持传递不太好的方式的属性。
    猜你喜欢
    • 2015-05-16
    • 2015-08-30
    • 1970-01-01
    • 1970-01-01
    • 2021-11-21
    • 1970-01-01
    • 2015-06-01
    • 1970-01-01
    • 2019-11-20
    相关资源
    最近更新 更多