问题:我正在使用Apollo Client,并以“ / deck / 2”这样的形式呈现了牌组,我想随机洗牌,并一次只显示一张一个按钮以查看下一个。每当状态改变时,我就一直在使用React重新渲染(我的onClick索引计数器+1),这一直困扰着我,因为shuffledCards变量在查询中,所以它会重新洗牌。我不确定如何防止这种情况发生。
我如何才能重新整理列表,而不必担心会在按钮的“ onClick”上重新整理它们。我想有一种方法可以在渲染之外获取随机数组,我可以在Regular反应中进行此操作,但是我很想了解使用Apollo查询的方法。
这是我在React和Graphql上对Apollo缺乏经验的绊脚石,而我还没有找到类似的Apollo graphql项目可以借鉴。我不能在对象上使用map,但是也许有一种方法可以使用map一次显示数组的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>
);
}
}
```
我将感谢您提供的任何帮助。谢谢!
答案 0 :(得分:0)
我不了解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>
)
}
}
答案 1 :(得分:0)
您在渲染函数中调用this.shuffle()-因此它将在每个渲染器上随机播放。
将其移到构造函数中,它只会被调用一次。
constructor(props) {
super(props);
this.state = {
index: 0
};
this.goToNext = this.goToNext.bind(this);
const CardsToRender = data.deck.cards;
}