Проблема: Я использую Apollo Client, и колода визуализируется как эта «/ deck / 2», и я хочу случайным образом перемешать карты и отображать только по одной с кнопкой для просмотра следующей. Я продолжаю сталкиваться с проблемой повторного рендеринга React каждый раз, когда изменяется состояние (мой счетчик индекса onClick +1), который перетасовывает карты, поскольку переменная shuffledCards находится внутри запроса. Я не знаю, как этого избежать.
Как я могу перетасовать список, не беспокоясь о том, что они будут перетасованы "onClick" кнопки. Я полагаю, что есть способ получить рандомизированный массив за пределами рендеринга, который я могу сделать в Regular response, но с помощью запросов Apollo, которые я не могу понять.
Здесь я спотыкаюсь из-за моего неопытности в React и Graphql с Apollo, и я не нашел похожего проекта Apollo graphql, на который можно было бы опираться. Я не могу использовать карту для объекта, но, может быть, есть способ использовать карту для отображения 1 объекта массива за раз? Не нашел рабочего решения.
Что я собираюсь случиться: я просто хочу визуализировать перетасованный массив карточек по одной, и нажатие следующей кнопки должно перемещать карточки в рандомизированном массиве без повторного рендеринга всякий раз, когда я нажимаю кнопку, иначе карточки будут повторяться в случайном порядке.
Вот код:
import React, { Component, Fragment } from "react";
```
import CardItem from "./CardItem";
const CARDS_QUERY = gql`
query CardsQuery($id: ID!) {
```
`;
export class Cards extends Component {
constructor(props) {
super(props);
this.state = {
index: 0
};
this.goToNext = this.goToNext.bind(this);
}
goToNext() {
this.setState({
index: this.state.index + 1
});
}
shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
render() {
let { id } = this.props.match.params;
id = parseInt(id);
return (
<Fragment>
<Query query = {CARDS_QUERY} variables = {{ id }}>
{({ data, error, loading }) => {
if (loading) {
return <Loading />;
}
if (error)
}
const CardsToRender = data.deck.cards;
//This variable gets reshuffled at every re-render
const shuffledCards = this.shuffle(CardsToRender);
//Single item to be returned
const item = shuffledCards[this.state.index];
if (this.state.index >= shuffledCards.length) {
return (
<div>
<h1>Finished</h1>
</div>
);
} else {
return (
<Fragment>
// Here I can get one item to display, but if I press next, the state changes which fires a re-render,
//shuffling the cards once more. My intention is to only shuffle cards at first render until the browser page is
//refreshed or user navigates away
<h1>{item.front}</h1>
<h1>{item.back}</h1>
//My second attempt is to map out the cards, but I have only been able to render a list,
// but not one at a time. Maybe there is a simple code solution in my .map to display
//one at a time without needing to change state?
{shuffledCards.map(card => (
<CardItem key = {card.id} card = {card} />
))}
<p>
<button onClick = {this.goToNext}>Next</button>
</p>
</Fragment>
);
}
}}
</Query>
</Fragment>
);
}
}
```
Буду благодарен за любую оказанную помощь. Спасибо!





Я не знаю об Appolo Query, но проблема, о которой вы упомянули, больше связана с React. Вы можете попробовать ниже один, чтобы не тасовать карты при каждом повторном рендеринге.
Реорганизуйте ваш компонент на две части.
1) ShuffleCards.js (дайте любое имя по своему усмотрению :)) - переместите свой компонент в «ShuffleCards» и передайте перемешанные шнуры дочернему компоненту, где вы можете обновить состояние для рендеринга следующей карты.
// ShuffledCards.js
import React, { Component, Fragment } from "react";
```
import CardItem from "./CardItem";
const CARDS_QUERY = gql`
query CardsQuery($id: ID!) {
```
`;
export class ShuffleCards extends Component {
constructor(props) {
super(props);
}
shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
render() {
let { id } = this.props.match.params;
id = parseInt(id);
return (
<Fragment>
<Query query = {CARDS_QUERY} variables = {{ id }}>
{({ data, error, loading }) => {
if (loading) {
return <Loading />;
}
if (error) {
}
const CardsToRender = data.deck.cards;
const shuffledCards = this.shuffle(CardsToRender);
return (
<Cards shuffledCards = {shuffledCards} />
);
}}
</Query>
</Fragment>
);
}
}
Переместите код, который обрабатывает отображаемую карту и обновляет состояние, в компонент «Карты».
import React, { Component, Fragment } from "react";
import CardItem from "./CardItem";
export class Cards extends Component {
constructor(props) {
super(props);
this.state = {
index: 0
};
this.goToNext = this.goToNext.bind(this);
}
goToNext() {
this.setState({
index: this.state.index + 1
});
}
render() {
const {shuffledCards} = this.props || [];
return (
<div>
{
this.state.index >= shuffledCards.length ?
<div>
<h1>Finished</h1>
</div>
:
<Fragment>
<h1>{item.front}</h1>
<h1>{item.back}</h1>
{
shuffledCards.map(card => (
<CardItem key = {card.id} card = {card} />
))
}
<p>
<button onClick = {this.goToNext}>Next</button>
</p>
</Fragment>
}
</div>
)
}
}
Вы вызываете this.shuffle () в своей функции рендеринга - поэтому он будет перемешиваться при каждом рендеринге.
Переместите это в свой конструктор, и он будет вызван только один раз.
constructor(props) {
super(props);
this.state = {
index: 0
};
this.goToNext = this.goToNext.bind(this);
const CardsToRender = data.deck.cards;
}