首页 > 解决方案 > 如何等到流星订阅数据完全完成?

问题描述

我一直在查看很多问题/答案以及流星文档,但仍然遇到流星反应问题并等待订阅数据完全加载。我正在尝试做两件不同的事情,一件是如果从数据库收到的数组小于在客户端创建的数组,然后做其他事情,如果它更大或相等。

问题出在我的订阅上,数据有时会通过管道传输,所以第一条语句总是至少触发一次。

我想知道在调用函数之前是否有办法确保数据库中的所有数据都已通过。

 if (import.length < arrayLength) {
      ...
 }
 if (import.length > arrayLength || import.length === arrayLength) {
      ...
 }

下面的完整代码示例

import { DropzoneDialog } from "material-ui-dropzone";
import React, { useState, useEffect, useRef } from "react";
import Button from "@material-ui/core/Button";
import { Cards } from "../../../../both/collections";
import { withTracker } from "meteor/react-meteor-data";
import { makeStyles } from "@material-ui/core/styles";
import { Meteor } from 'meteor/meteor';
import _, { map, object } from 'underscore';

const useStyles = makeStyles({
  importButton: {
    color: "#C8C8C8",
    borderColor: "#C8C8C8",
    "&:hover": {
      backgroundColor: "rgba(72,72,72,0.7)",
      borderColor: "rgba(255, 255, 255)",
      color: "rgba(255, 255, 255)",
    },
  },
});

function importDeck(files, setDeckIsReady, setDeckLength, setCountObj, sub, setSubscription) {

  let reader = new FileReader();
  reader.readAsText(files[0]);

  reader.onload = function () {
    let cardArray = reader.result.split("\n");
    let cardArrayFiltered = cardArray.filter(Boolean)
    const countObj = cardArrayFiltered.reduce((acc, next) => {
      count = next.substr(0, next.indexOf(' ')); 
      name = next.substr(next.indexOf(' ') + 1); 
      return {...acc, [name]: count}
    }, {});
    sub.stop()
    const subscription = Meteor.subscribe('cardSearchTwo', Object.keys(countObj), {onReady() {setDeckIsReady(true)}})
    setDeckLength(Object.keys(countObj).length)
    setCountObj(countObj) 
    setSubscription(subscription)
  };
}

export function DeckImport({importCards, setCurrentDeck, importDeckFinal, search, setSearch}) {
  const classes = useStyles()
  const [open, setOpen] = useState(false);
  const [deckIsReady, setDeckIsReady] = useState(false);
  const [deckLength, setDeckLength] = useState(0);
  const [countObj, setCountObj] = useState({});
  const [subscription, setSubscription] = useState({stop(){}})
  const [isReady, setIsReady] = useState(false)
  console.log(deckIsReady, importCards)
  let missingCards = []
  let fixedDeck = importCards
  useEffect(() => {
    if (importCards.length < deckLength && deckIsReady) {
      fixedDeck = []
      Object.keys(countObj).forEach(card => {
        if (!importCards.some(e => e.name === card)) missingCards.push(card)
      }) 
      fixedDeck = importCards.filter(card => !missingCards.includes(card.name))
      console.log('LESS THAN', fixedDeck.length,  missingCards.length)
      importDeckFinal(fixedDeck, setCurrentDeck, countObj) 
      setDeckIsReady(false) 
      setDeckLength(0) 
      setCountObj({})
    }
    if ((importCards.length > deckLength || importCards.length === deckLength)  && deckIsReady) {
      fixedDeck = []
      importCards.forEach(card => {
        if (card.name in countObj) fixedDeck.push(card)
      })
      console.log('EQUAL', fixedDeck);
      importDeckFinal(fixedDeck, setCurrentDeck, countObj) 
      setDeckIsReady(false) 
      setDeckLength(0) 
      setCountObj({})
    }
  })
  return (
    <div>
      <Button
        variant="outlined"
        color="primary"
        component="label"
        onClick={() => setOpen(true)}
        className={classes.importButton}>
        Import
        </Button>
        <DropzoneDialog
        acceptedFiles={["text/*"]}
        cancelButtonText={"cancel"}
        submitButtonText={"submit"}
        maxFileSize={5000000}
        open={open}
        onClose={() => setOpen(false)}
        onSave={(files) => {
            
            importDeck(
            files,
            setDeckIsReady,
            setDeckLength,
            setCountObj,
            subscription,
            setSubscription,
            setIsReady
            );
          setOpen(false);
          }}
        showPreviews={true}
        showFileNamesInPreview={true}
        />
    </div>
  )
}

export default withTracker(props => {
  const cards = Cards.find({}, { sort: {name: 1}}).fetch();
  const uniqueNames = _.uniq(cards.map(function(x) {return x.name;}), true)
  return {
    importCards: uniqueNames.map(name => cards.find(({ name: cName }) => cName === name))
  };
})(DeckImport);

具体来说,deckIsReady 被设置为 True 当只有一部分数据完全准备好时。

标签: javascriptreactjsmeteor

解决方案


你可以使用两件事

  • useTrackerReact 钩子是meteor-react-data. 每当响应式数据源更改时,此挂钩就会重新运行。

  • 使用Subscription.ready()来确定数据源是否已准备好使用。

把它放在一起,比如:

const importReady = useTracker(() => Meteor.subscribe('cardSearchTwo').ready())

推荐阅读