Javascript 如何等待流星订阅数据完全完成?

Javascript 如何等待流星订阅数据完全完成?,javascript,reactjs,meteor,Javascript,Reactjs,Meteor,我已经浏览了很多问题/答案以及meteor文档,但在meteor react方面仍然存在问题,并等待订阅数据完全加载。我尝试做两件不同的事情,一件是从数据库接收的数组小于客户端创建的数组,另一件是更大或相等的数组 问题在于我的订阅,数据一次传输一点,所以第一条语句总是至少触发一次 我想知道在调用函数之前,是否有办法确保数据库中的所有数据都已通过 if (import.length < arrayLength) { ... } if (import.length >

我已经浏览了很多问题/答案以及meteor文档,但在meteor react方面仍然存在问题,并等待订阅数据完全加载。我尝试做两件不同的事情,一件是从数据库接收的数组小于客户端创建的数组,另一件是更大或相等的数组

问题在于我的订阅,数据一次传输一点,所以第一条语句总是至少触发一次

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

 if (import.length < arrayLength) {
      ...
 }
 if (import.length > arrayLength || import.length === arrayLength) {
      ...
 }
if(import.lengtharrayLength | | 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);
从“物料ui dropzone”导入{DropzoneDialog};
从“React”导入React,{useState,useffect,useRef};
从“@物料界面/核心/按钮”导入按钮;
从“../../../../both/collections”导入{Cards}”;
从“流星/反应流星数据”导入{withTracker};
从“@material ui/core/styles”导入{makeStyles}”;
从“流星/流星”导入{Meteor};
从“下划线”导入{map,object};
const useStyles=makeStyles({
导入按钮:{
颜色:#C8C8C8”,
边框颜色:“C8C8C8”,
“&:悬停”:{
背景色:“rgba(72,72,72,0.7)”,
边框颜色:“rgba(255,255,255)”,
颜色:“rgba(255,255,255)”,
},
},
});
函数importDeck(文件、setDeckIsReady、setDeckLength、setCountObj、sub、setSubscription){
let reader=new FileReader();
reader.readAsText(文件[0]);
reader.onload=函数(){
让cardArray=reader.result.split(“\n”);
让cardArrayFiltered=cardArray.filter(布尔值)
const countObj=cardArrayFiltered.reduce((acc,next)=>{
count=next.substr(0,next.indexOf(“”));
name=next.substr(next.indexOf(“”)+1);
返回{…acc[name]:count}
}, {});
副主席(译文)
const subscription=Meteor.subscripte('cardSearchTwo',Object.keys(countObj),{onReady(){setDeckIsReady(true)}})
setDeckLength(Object.keys(countObj.length)
setCountObj(countObj)
设置订阅(订阅)
};
}
导出函数DeckImport({importCards,setCurrentDeck,importDeckFinal,search,setSearch}){
常量类=useStyles()
const[open,setOpen]=useState(false);
const[deckIsReady,setDeckIsReady]=使用状态(false);
常数[deckLength,setDeckLength]=使用状态(0);
const[countObj,setCountObj]=useState({});
常量[subscription,setSubscription]=useState({stop(){}})
常量[isReady,setIsReady]=useState(false)
控制台日志(deckIsReady、importCards)
让丢失的卡片=[]
让fixedDeck=importCards
useffect(()=>{
if(importCards.length{
如果(!importCards.some(e=>e.name==card))丢失了cards.push(card)
}) 
fixedDeck=importCards.filter(卡片=>!missingCards.includes(卡片名称))
console.log('小于',fixedDeck.length,missingCards.length)
importDeckFinal(fixedDeck、setCurrentDeck、countObj)
setDeckIsReady(错误)
setDeckLength(0)
setCountObj({})
}
如果((importCards.length>deckLength | | importCards.length==deckLength)&&deckIsReady){
fixedDeck=[]
importCards.forEach(卡片=>{
if(countObj中的card.name)fixedDeck.push(卡片)
})
console.log('EQUAL',fixedDeck);
importDeckFinal(fixedDeck、setCurrentDeck、countObj)
setDeckIsReady(错误)
setDeckLength(0)
setCountObj({})
}
})
返回(
setOpen(真)}
className={classes.importButton}>
进口
setOpen(false)}
onSave={(文件)=>{
进口检验(
文件夹,
赛德克已经准备好了,
设定长度,
setCountObj,
订阅
设置订阅,
setIsReady
);
setOpen(假);
}}
showPreviews={true}
showFileNamesInPreview={true}
/>
)
}
使用跟踪器导出默认值(道具=>{
const cards=cards.find({},{sort:{name:1}}).fetch();
const uniqueNames=uq.uniq(cards.map(函数(x){return x.name;}),true)
返回{
importCards:uniqueNames.map(name=>cards.find({name:cName}=>cName==name))
};
})(进口);

具体地说,deckIsReady在只有一部分数据真正准备就绪时被设置为True。

我更熟悉正常的meteor订阅/发布逻辑,不太清楚您的react逻辑会如何影响事情

有了这句话,
const subscription=Meteor.subscripte('cardSearchTwo',Object.keys(countObj),{onReady(){setDeckIsReady(true)}})
这一行会被多次重新运行,在回调中,您只是设置了甲板准备就绪的期望值,但是,在回调之外,您正在设置组长度和其他与您刚才所做的订阅相关的内容—这意味着这些行将在订阅从发布函数获取数据之前发生

 if (import.length < arrayLength) {
      ...
 }
 if (import.length > arrayLength || import.length === arrayLength) {
      ...
 }
因此,这里的解决方案是将那些与数据组相关的内容放在回调中,以便正确设置它们,然后在最后通过调用
setDeckIsReady(true)
触发反应式
DeckImport
,您可以使用两个内容

  • useTracker
    React钩子,它是流星反应数据的一部分。每当反应式数据源发生更改时,此挂钩将重新运行

  • 使用
    Subscription.ready()

把它放在一起,比如:

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

您可以使用方法从服务器获取数据。逻辑比usin简单