RefluxJS“单"店铺? [英] RefluxJS "single" store?

查看:85
本文介绍了RefluxJS“单"店铺?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Well.. I created a upload component, when user upload a image the component show a image preview using FileReader API.

但是,如果我在另一个组件中使用了3个组件,那么当我上传图片时,该图片也会在3个组件中重复出现.

But, if I've been using 3 components in another component, when I upload a image, this image is also repeated in 3 components.

示例:

... 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;

结果:

有什么主意吗?

谢谢!

推荐答案

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

unfortunately the store behaves like a singleton, i.e there is only one UploadStore instance.

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

What you can do is introduce an extra parameter to keep the uploads apart. Your store will now take an array of uploads but each upload is tagged with the category and your component will also have a category and takes only the images from the store which belong to the same category. This is done using the Reflux.connectFilter mixin.

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

First, I'd separate the uploaded image into its own component like this:

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组件中进行一些更改,以便按类别进行过滤:

Then we have to change some stuff inside your UploadImage component so that it will filter by category:

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>
    );
  }
});

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

And your store now holds an array of "file" objects, each tagged with a category:

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组件:

And finally in your view you can use the UploadImage component like this:

我即时编写了代码,因此可能会有一些问题-但这更多的是关于概念的.同样,现在可以为每个类别上传一个以上的图像,如果不希望这样做,请考虑使用哈希映射替换商店中的数组,以便键对应于类别-然后只能在其中上传一个图像.每个类别.

I wrote the code on the fly, so there might be some issues - but it's more about the concept. Also it's possible to upload more than one image per category now, if this is not desired, then think about replacing the array in the store with a hash map, so that the keys correspond to the category - then only one image can be uploaded in each category.

回答您的评论

也许您可以摆脱商店的工厂方法,即像这样:

Maybe you could get away with a factory method for the store, i.e something like this:

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 */
});

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

but I suspect that your action will trigger all instances of your upload stores, but it's worth a try. But this comes with a lot of drawback such as other components can not listen to this store easily.

stackoverflow中,提出了类似的问题,并且提出了正确的概念方法所有人都使用一个存储桶/存储,并在存储中对商品进行标记,以便将它们分开.

In this stackoverflow a similar question is asked, and also the conceptual right way to do it is to use one bucket/store for all and keep the items in the store tagged so that you can keep them apart.

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

Keep in mind that stores also get cleared an refilled, as an example if you create a web-shop with products and different categories, you clear and refill the ProductStore everytime the user switches to another category. If you additionally have a sidebar that maybe shows "Products you might like" then I'd model this as a separate store, i.e the ProductSuggestionStore but both contains objects of the type "Product".

如果商店在语义上有所不同,但是共享许多上载逻辑,那么您还可以尝试为商店建立基本的原型/类,然后扩展特定的商店或将上载逻辑外包给服务类.

If the stores behave semantically different but share a lot of the upload logic you could also try to build a base prototype/class for your stores and then extend the specific stores or outsource the upload logic into a service class.

如果您担心性能,即一次上传会导致所有组件重新呈现,则可以在shouldComponentUpdate中添加检查.

If you are worried about performance, i.e one upload causes all components to re-render, then you can add a check within shouldComponentUpdate.

一个很好的示例,为什么只使用一个商店,可能是用户想要关闭窗口,但在您网站上的某个地方仍在上传中,因此您的主应用程序视图只需要检查一个商店.另外,由于所有上载都通过一家商店进行,因此上载可以轻松地排队,以免占用带宽.

A good example why to use only one store might be the case where the user wants to close the window but somewhere on your website an upload is still pending, then your main application view just has to check one store. Also uploads could be queued easily so that the bandwidth is not exhausted, since all uploads go through one store.

还请记住,您可以让商店监听其他商店,例如,UploadHistoryStore保留最近10次上传的带有时间戳的记录.所有上传内容都放在同一个存储桶中,但是如果您有最近10次上传"组件,则只需收听"UploadHistoryStore"

Also keep in mind you can have stores that listen to other stores, as an example the UploadHistoryStore keeps a timestamped record of the last 10 uploads. All uploads go into the same bucket but if you have a "Last 10 Uploads" component it just has to listen to the "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);
    }
});

这篇关于RefluxJS“单"店铺?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆