#javascript #reactjs #meteor
#javascript #reactjs #meteor
Вопрос:
Я просматривал множество вопросов / ответов, а также документы meteor, но у меня все еще возникают проблемы с meteor react, и я жду, пока данные подписки не будут полностью загружены. Я пытаюсь сделать две разные вещи: одну, если массив, полученный из базы данных, меньше, чем массив, созданный на стороне клиента, а затем сделать что-то еще, если он больше или равен.
Проблема связана с моей подпиской, данные передаются по конвейеру понемногу, поэтому первое утверждение всегда будет срабатывать по крайней мере один раз.
Мне было интересно, есть ли способ убедиться, что все данные из базы данных прошли, прежде чем вызывать функцию.
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",
"amp;: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 amp;amp; 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) amp;amp; 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, когда фактически готова только часть данных.
Комментарии:
1. Вы можете использовать метод для извлечения данных с сервера. Логика проще, чем при использовании подписок, и более высокая производительность.
Ответ №1:
Я больше знаком с обычной логикой подписки / публикации meteor, не совсем уверен, как ваша логика реагирования влияет на вещи.
С учетом сказанного const subscription = Meteor.subscribe('cardSearchTwo', Object.keys(countObj), {onReady() {setDeckIsReady(true)}})
эта строка запускается повторно несколько раз, и в обратном вызове вы просто устанавливаете ожидания готовности колоды, но затем за пределами обратного вызова вы устанавливаете длину колоды и другие вещи, связанные с подпиской, которую вы только что сделали — это означает, что эти строки будут выполняться до этой подпискизакончил получение данных из функции публикации.
Итак, решение здесь состоит в том, чтобы поместить те вещи, которые связаны с колодой, в обратный вызов, чтобы их можно было правильно установить, а затем в конце вызвать реактивный DeckImport
вызов setDeckIsReady(true)
.
Ответ №2:
Вы можете использовать две вещи
-
useTracker
Реагирующий хук, который является частьюmeteor-react-data
. Этот перехват запускается повторно всякий раз, когда изменяется реактивный источник данных. -
используйте
Subscription.ready()
, чтобы определить, готов ли источник данных к использованию.
Соедините это что-то вроде:
const importReady = useTracker(() => Meteor.subscribe('cardSearchTwo').ready())